Go WebSocket server: Use of closed network connection - go

I'm working on a websocket server and for some reason it outputs:
"WSARecv tcp 127.0.0.1:8080: Use of closed network connection."
I don't know why it says that because I haven't closed the connection at any point in time.
Below are some source code files of the server. If needed, the full source code is here on GitHub.
connection.go
package net
import (
"log"
"golang.org/x/net/websocket"
pnet "kekocity/misc/packet"
"kekocity/interfaces"
)
type Connection struct {
socket *websocket.Conn
txChan chan pnet.INetMessageWriter
rxChan chan pnet.INetMessageReader
user interfaces.IUser
}
func NewConnection(_socket *websocket.Conn) *Connection {
// The pointer allow us to modify connection struct from outside
connection := &Connection{
socket: _socket,
txChan: make(chan pnet.INetMessageWriter),
rxChan: make(chan pnet.INetMessageReader),
}
go connection.ReceivePoller()
go connection.SendPoller()
return connection
}
func (c *Connection) AssignToUser(_user interfaces.IUser) {
if _user == nil {
panic("net.connection: the user interface can not be nil!")
return
}
c.user = _user
_user.SetNetworkChans(c.rxChan, c.txChan)
}
/*
* ReceivePoller and SendPoller starts listening when the first packet is verified and the new connection is started
*/
func (c *Connection) ReceivePoller() {
for {
packet := pnet.NewPacket()
var buffer []uint8
err := websocket.Message.Receive(c.socket, &buffer)
if err == nil {
copy(packet.Buffer[0:len(buffer)], buffer[0:len(buffer)])
c.parsePacket(packet)
} else {
println(err.Error())
break
}
}
}
func (c *Connection) SendPoller() {
for {
// Read messages from transmit channel
message := <-c.txChan
if message == nil {
log.Println("SenPoller", "The message is nil, break the loop")
break
}
// Convert netmessage to packet
packet := message.WritePacket()
packet.SetHeader()
// Create byte buffer
buffer := packet.GetBuffer()
data := buffer[0:packet.GetMsgSize()]
// Send bytes off to the internetz
websocket.Message.Send(c.socket, data)
}
}
func (c *Connection) parsePacket(_packet pnet.IPacket) {
log.Println("net.connection:", "Received new packet!")
}
func (c *Connection) Close() {
// Close channels
close(c.txChan)
close(c.rxChan)
// Close the socket
c.socket.Close()
c.user = nil
}
server.go
package net
// <imports>
import (
"log"
"fmt"
"net/http"
"golang.org/x/net/websocket"
pnet "kekocity/misc/packet"
cmap "kekocity/misc/concurrentmap"
"kekocity/data/helpers"
"kekocity/net/message"
)
var server *Server
type Server struct {
port int
connectedUsers *cmap.ConcurrentMap
}
func init() {
server = newServer()
}
func newServer() *Server {
return &Server{
port: 8080,
connectedUsers: cmap.New(),
}
}
func Listen(_port int) {
server.port = _port
log.Printf("Listening for connections on port %d!", _port)
http.Handle("/ws", websocket.Handler(clientConnection))
err := http.ListenAndServe(fmt.Sprintf(":%d", _port), nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
func clientConnection(clientsock *websocket.Conn) {
packet := pnet.NewPacket()
buffer := make([]uint8, pnet.PACKET_MAXSIZE)
recv, err := clientsock.Read(buffer)
if err == nil {
copy(packet.Buffer[0:recv], buffer[0:recv])
parseFirstMessage(clientsock, packet)
} else {
if err.Error() != "EOF" {
log.Println("net.server", "Client connection error:", err.Error())
}
}
}
func parseFirstMessage(_conn *websocket.Conn, _packet *pnet.Packet) {
_message := _packet.ToString()
// If the first packet length is < 1 close the socket
if len(_message) < 1 {
_conn.Close()
return
}
// Create the connection
connection := NewConnection(_conn)
// Authentication wrapper
authPacket := &message.AuthMessage{}
user, err := helpers.AuthHelper.AuthenticateUsingCredentials(_message)
if err != nil {
log.Fatal("Invalid credentials!")
authPacket.Status = "error"
} else {
// Need to check if its already logged
authPacket.Status = "success"
connection.AssignToUser(user)
connection.txChan <- authPacket
return
}
// Send bad auth message and close
connection.txChan <- authPacket
connection.Close()
}
Full source code: github

The context in the request is canceled right after the handler finishes.
serverHandler{c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
This is the reason why your context. In this diagram, you can find how the context is created in the server.Serve method.
It is described in more detail in the blog post: HTTP context livetime.
In the websocket, the situation is very similar. The context is closed just after the handler finishes.
func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
rwc, buf, err := w.(http.Hijacker).Hijack()
if err != nil {
panic("Hijack failed: " + err.Error())
}
// The server should abort the WebSocket connection if it finds
// the client did not send a handshake that matches with protocol
// specification.
defer rwc.Close() // <- here! It's executed when the s.Handler(conn) exites
conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake)
if err != nil {
return
}
if conn == nil {
panic("unexpected nil conn")
}
s.Handler(conn)
}
To fix that, you can create a new context from context.Background(), add some timeouts if needed and use it instead.

Related

Go websocket test acting strange

So basically I'm writing a go test for my chat application and for some reason the if I write Test_saveMessage function in the top of this file my tests go through and they work fine, however if I write the Test_InitRouter in the top of this file - my server opens and the test doesn't finish. As if it would be listening for more requests. Does anyone know the reason of why this could be happening? Here is the that does not work code:
package messenger
import (
"fmt"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"net/http/httptest"
"strings"
"testing"
)
var testMessage = Message{
Username: "Name",
Message: "Test message"}
//Tests InitRouter both sending and receiving messages
func Test_InitRouter(t *testing.T) {
var receivedMessage Message
//Create test server with the InitRouter handler
s := httptest.NewServer(InitRouter())
defer s.Close()
// Convert URL from http to ws
u := "ws" + strings.TrimPrefix(s.URL, "http")
fmt.Println(u)
// Connect to the test server
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
if err != nil {
t.Fatalf("%v", err)
}
defer ws.Close()
//Send message to the server read received message and see if it's the same
if err != ws.WriteJSON(testMessage) {
t.Fatalf("%v", err)
}
err = ws.ReadJSON(&receivedMessage)
if err != nil {
t.Fatalf("%v", err)
}
if receivedMessage != testMessage {
t.Fatalf("%v", err)
}
}
//Test for the saveMessage function
func Test_saveMessage(t *testing.T) {
saveMessage(testMessage)
assert.Equal(t, 1, len(messages), "Expected to have 1 message")
}
As soon as I move the Test_saveMessage function to the top it starts working properly.
Here is the code for the handler:
package messenger
import (
"fmt"
"github.com/go-chi/chi"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
"net/http"
)
func InitRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", GetWebsocket)
return r
}
var clients = make(map[*websocket.Conn]bool) // connected clients
var broadcast = make(chan Message) // broadcast channel
var messages = []Message{}
func GetWebsocket(w http.ResponseWriter, r *http.Request) {
// Upgrade initial GET request to a websocket
upgrader := websocket.Upgrader{}
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Error(err)
}
// Close the connection when the function returns
defer ws.Close()
// Register our new client and send him the chat history
clients[ws] = true
serveInitialMessages(ws)
//initialize message sending logic
sendMessages(ws)
}
// Sends messages from a particular websocket to the channel
func sendMessages(ws *websocket.Conn){
for {
var msg Message
// Read in a new message as JSON and map it to a Message object
err := ws.ReadJSON(&msg)
if err != nil {
log.Info(err)
delete(clients, ws)
break
}
// Send the newly received message to the broadcast channel
broadcast <- msg
saveMessage(msg)
}
}
func HandleMessages() {
for {
// Grab the next message from the broadcast channel
msg := <-broadcast
fmt.Println(msg)
// Send it out to every client that is currently connected
for client := range clients {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
delete(clients, client)
}
}
}
}
func saveMessage(m Message) {
if len(messages) >= 50 {
messages = messages[1:]
}
messages = append(messages, m)
}
func serveInitialMessages(ws *websocket.Conn) {
for _, m := range messages {
err := ws.WriteJSON(m)
if err != nil {
log.Error(err)
}
}
}

How to isolate a close 1006 (abnormal closure): unexpected EOF

I've been trying to subscribe to this API feed: https://www.cryptofacilities.com/resources/hc/en-us/articles/360000578294-Fills
Whenever I try to subscribe, it looks like I'm able to send the payload with WRITEJSON, but what I receive from my Read method is
websocket: close 1006 (abnormal closure): unexpected EOF.
The full terminal message is:
2019/08/04 22:20:31 recv: {"event":"info","version":1}
2019/08/04 22:20:31 recv: {"event":"subscribed","feed":"heartbeat"}
2019/08/04 22:20:31 recv: {"event":"challenge","message":"c6e55c07-d07a-4560-9283-be75ee458433"}
^C2019/08/04 22:21:50 interrupt
2019/08/04 22:21:50 write close: write tcp 192.168.1.6:49624->104.16.51.17:443: write: broken pipe
I understand from here that the status code means that my client closed the connection. I'm unable to track down this problem.
I've tried running race detection, turning off firewall (I have a Mac, and tried to turn off the antivirus - found out it's built in. Would this be worth pursuing?) increasing the handshake timeout, handling the error with a close frame, a different OS, increasing the buffer size, and max message size. A lot of it in reference to this post:
https://github.com/gorilla/websocket/issues/321
This is my client:
package websocket
import (
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"github.com/gorilla/websocket"
)
var addr = "wss://www.cryptofacilities.com/ws/v1"
var Wait = 50000 * time.Second
var MaxMessageSize int64 = 100000
//WebSocket connection struct to pass into other methods
type WebSocket struct {
Conn *websocket.Conn
}
//Message represents the public server push messages
type Message struct {
Event string
Feed string
Message interface{}
}
type ChallengeSub struct {
Event string `json:"event"`
Message string `json:"message"`
}
type HeartBeat struct {
Event string
Message string
}
type FillSubscribe struct {
Event string `json:"event"`
Feed string `json:"feed"`
APIKey string `json:"api_key"`
OriginalChallenge string `json:"original_challenge"`
SignedChallenge string `json:"signed_challenge"`
}
//OpenWebSocket Connects to kraken API and returns a connection
func OpenWebSocket() (*WebSocket, error) {
conn, response, err := websocket.DefaultDialer.Dial(addr, nil)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusSwitchingProtocols {
return nil, fmt.Errorf("failed to upgrade protocol to websocket")
}
return &WebSocket{Conn: conn}, nil
}
//HeartBeat subscribes to the pingpong feed to keep the connection alive
func (c *WebSocket) HeartBeat() error {
ping := map[string]interface{}{
"event": "subscribe",
"feed": "heartbeat",
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(ping)
return err
}
func (c *WebSocket) Next() ([]byte, error) {
_, payload, err := c.Conn.ReadMessage()
return payload, err
}
//Challenge requests the UUID from kraken API for auth handshake
func (c *WebSocket) Challenge() error {
challengeRequest := map[string]interface{}{
"event": "challenge",
"api_key": "rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m",
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(challengeRequest)
if err != nil {
log.Println("write:", err)
return err
}
return err
}
func (c *WebSocket) Decode(payload []byte) (string, string) { //Decode takes in a connection and reference to Message struct
var msg json.RawMessage
env := Message{
Message: &msg,
}
if err := json.Unmarshal([]byte(payload), &env); err != nil {
log.Fatal(err)
}
switch env.Event {
case "challenge":
var s ChallengeSub
if err := json.Unmarshal(msg, &s.Message); err != nil {
log.Fatal(err)
}
message, signed := c.Signature(s.Message)
c.FillSubscribe(message, signed)
return message, signed
case "info":
{
fmt.Println("Connected:", env.Event)
}
case "subscribed":
{
fmt.Println("Connected to Heartbeat")
}
default:
switch env.Feed {
case "heartbeat":
fmt.Println("Live")
}
}
return "No messages to Decode...", ""
}
func (c *WebSocket) Signature(message string) (string, string) {
secret64, _ := base64.StdEncoding.DecodeString("rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m")
hash := sha256.New()
hash.Write([]byte(message))
challenge256 := hash.Sum(nil)
hmacHash := hmac.New(sha512.New, secret64)
hmacHash.Write(challenge256)
secretChallenge := hmacHash.Sum(nil)
signed := base64.StdEncoding.EncodeToString(secretChallenge)
return message, signed
}
//FillSubscribe populates message struct and sends out the JSON message
func (c *WebSocket) FillSubscribe(challenge string, signed string) error {
fillMessage := map[string]interface{}{
"event": "subscribe",
"feed": "fills",
"api_key": "rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m",
"original_challenge": challenge,
"signed_challenge": signed,
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(fillMessage)
if err != nil {
log.Println("write:", err)
return err
}
return err
}
Here is my main program:
package main
import (
"fmt"
"log"
"github.com/Mjavala/KrakenAPI/websocket"
)
var message string
var signed string
func main() {
ws, err := websocket.OpenWebSocket()
if err != nil {
log.Fatal(err)
}
ws.HeartBeat()
ws.Challenge()
fmt.Println(message, signed)
for {
// We first read in a raw message. An error here is a socket level
// error.
payload, err := ws.Next()
if err != nil {
log.Fatalf("socket error: %+v\n", err)
}
log.Printf("recv: %s", payload)
message, signed = ws.Decode(payload)
if err != nil {
log.Fatalf("decode error: %+v\n", err)
}
}
}
I believe that even if the API Keys are wrong a return message should still be sent, as per the API; but instead I get that 1006 close frame.
I'm new to WebSockets. I'm able to get the challenge messaged and heartbeat subscription from the API, the problem is specific to the fill subscription.
Also if anyone wants to replicate this, I can give the full code/Git link.

TCP connection returns 'broken pipe' error when used multiple times

This question relates to go and its net package.
I wrote a simple tcp server handles some RPC. the client is using a chan net.Conn to manage all tcp connection on the client side. Server is running with a tcp listener.
here's the code:
client:
package server
import (
"errors"
"log"
"net"
)
var tcpPool chan net.Conn
func NewClient(connections int, address string) {
tcpPool = make(chan net.Conn, connections)
for i := 0; i < connections; i++ {
conn, err := net.Dial("tcp4", address)
if err != nil {
log.Panic(err)
}
tcpPool <- conn
}
}
func SendMessage(msg []byte) ([]byte, error) {
conn := getConn()
log.Println("check conn: ", conn)
log.Println("msg: ", msg)
defer releaseConn(conn)
// send message
n, err := conn.Write(msg)
if err != nil {
log.Panic(err)
} else if n < len(msg) {
log.Panic(errors.New("Message did not send in full"))
}
// receiving a message
inBytes := make([]byte, 0)
for {
// bufsize 1024, read bufsize bytes each time
b := make([]byte, bufSize)
res, err := conn.Read(b)
log.Println("server sends >>>>>>>>>>>>: ", res)
if err != nil {
b[0] = ReError
break
}
inBytes = append(inBytes, b[:res]...)
// message finished.
if res < bufSize {
break
}
}
// check replied message
if len(inBytes) == 0 {
return []byte{}, errors.New("empty buffer error")
}
log.Println("SendMessage gets: ", inBytes)
return inBytes, nil
}
func releaseConn(conn net.Conn) error {
log.Println("return conn to pool")
select {
case tcpPool <- conn:
return nil
}
}
func getConn() (conn net.Conn) {
log.Println("Take one from pool")
select {
case conn := <-tcpPool:
return conn
}
}
server
func StartTCPServer(network, addr string) error {
listener, err := net.Listen(network, addr)
if err != nil {
return errors.Wrapf(err, "Unable to listen on address %s\n", addr)
}
log.Println("Listen on", listener.Addr().String())
defer listener.Close()
for {
log.Println("Accept a connection request.")
conn, err := listener.Accept()
if err != nil {
log.Println("Failed accepting a connection request:", err)
continue
}
log.Println("Handle incoming messages.")
go onConn(conn)
}
}
//onConn recieves a tcp connection and waiting for incoming messages
func onConn(conn net.Conn) {
inBytes := make([]byte, 0)
defer func() {
if e := recover(); e != nil {
//later log
if err, ok := e.(error); ok {
println("recover", err.Error())
}
}
conn.Close()
}()
// load msg
for {
buf := make([]byte, bufSize)
res, err := conn.Read(buf)
log.Println("server reading: ", res)
inBytes = append(inBytes, buf[:res]...)
if err != nil || res < bufSize {
break
}
}
var req RPCRequest
err := json.Unmarshal(inBytes, &req)
if err != nil {
log.Panic(err)
}
log.Println("rpc request: ", req)
var query UserRequest
err = json.Unmarshal(req.Query, &query)
if err != nil {
log.Panic(err)
}
log.Println("rpc request query: ", query)
// call method to process request
// good now we can proceed to function call
// some actual function calls gets a output
// outBytes, err := json.Marshal(out)
conn.Write(outBytes)
}
I think this is very standard. but for some reason, I can only send message on the client side one, and then the follow 2nd and 3rd start to show some irregularity.
1st ---> success, gets response
2nd ---> client can send but nothing gets back, logs on server side shows no in coming message
3rd ---> if I send from client side one more time, it shows broken pipe error..
There are some bad handling way.
First, the flag to insure the msg from server finished is depending on io.EOF,not length
// message finished.
if res < 512 {
break
}
instead of this, reader returns an io.EOF is the only symbol that shows message finished.
Second, chan type has its property to block and not need to use select.by the way, you really need to start a goroutine to release. The same requirement for getConn
func releaseConn(conn net.Conn) {
go func(){
tcpPool <- conn
}()
}
func getConn() net.Conn {
con := <-tcpPool
return con
}
Third, listener should not be close, code below is bad
defer listener.Close()
The most important reason is
on the client side,
res, err := conn.Read(b) this receive the reply from the server.
when nothing reply ,it block rather than io.EOF, nor some error else.
It means ,you cann't box a lasting communicating part into a function send().
You can do a single thing to use sendmsg() to send, but never use sendmsg() to handle the reply.
you can handle reply like this
var receive chan string
func init() {
receive = make(chan string, 10)
}
func ReceiveMessage(con net.Conn) {
// receiving a message
inBytes := make([]byte, 0, 1000)
var b = make([]byte, 512)
for {
// bufsize 1024, read bufsize bytes each time
res, err := con.Read(b)
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err.Error())
break
}
inBytes = append(inBytes, b[:res]...)
msg := string(inBytes)
fmt.Println("receive msg from server:" + msg)
receive <- msg
}
}
I found several problem in your code, but I can't tell which one leads your failure.
This is my code according to what you write and did some fixing.
client.go:
package main
import (
"fmt"
"io"
"log"
"net"
)
var tcpPool chan net.Conn
var receive chan string
func init() {
receive = make(chan string, 10)
}
func NewClient(connections int, address string) {
tcpPool = make(chan net.Conn, connections)
for i := 0; i < connections; i++ {
conn, err := net.Dial("tcp", address)
if err != nil {
log.Panic(err)
}
tcpPool <- conn
}
}
func SendMessage(con net.Conn, msg []byte) error {
// send message
_, err := con.Write(msg)
if err != nil {
log.Panic(err)
}
return nil
}
func ReceiveMessage(con net.Conn) {
// receiving a message
inBytes := make([]byte, 0, 1000)
var b = make([]byte, 512)
for {
// bufsize 1024, read bufsize bytes each time
res, err := con.Read(b)
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err.Error())
break
}
inBytes = append(inBytes, b[:res]...)
msg := string(inBytes)
fmt.Println("receive msg from server:" + msg)
receive <- msg
}
}
func getConn() net.Conn {
con := <-tcpPool
return con
}
func main() {
NewClient(20, "localhost:8101")
con := <-tcpPool
e := SendMessage(con, []byte("hello, i am client"))
if e != nil {
fmt.Println(e.Error())
return
}
go ReceiveMessage(con)
var msg string
for {
select {
case msg = <-receive:
fmt.Println(msg)
}
}
}
server.go
package main
import (
"fmt"
"io"
"net"
)
func StartTCPServer(network, addr string) error {
listener, err := net.Listen(network, addr)
if err != nil {
return err
}
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err.Error())
continue
}
onConn(conn)
}
}
//onConn recieves a tcp connection and waiting for incoming messages
func onConn(conn net.Conn) {
inBytes := make([]byte, 0)
// load msg
for {
buf := make([]byte, 512)
res, err := conn.Read(buf)
if err != nil {
if err == io.EOF {
return
}
fmt.Println(err.Error())
return
}
inBytes = append(inBytes, buf[:res]...)
fmt.Println("receive from client:" + string(inBytes))
conn.Write([]byte("hello"))
}
}
func main() {
if e := StartTCPServer("tcp", ":8101"); e != nil {
fmt.Println(e.Error())
return
}
}
this works and no error.
By the way, I can't see where either on the client side or the server side you do con.Close(). It's nessasary to close it.This means a connection once got from the pool, you don't put it back. When you think a connection is over, then close it and build a new connection to fill the pool rather than put it back,beause it's a fatal operation to put a closed con back to the pool.

Using golang's defer in a separate method

I'm using the golang RabbitMQ library in a project, and I have a Connect function in a separate package. I'm calling Connect, in my main function, however because I connect to RabbitMQ in a separate function, the defer conn.Close() function is called, which closes the connection within the Connect function. Which makes perfect sense, but that begs the question, where then, do I call conn.Close()?
package drivers
import (
// Core
"log"
"os"
"time"
// Third party
"github.com/streadway/amqp"
)
type Queue struct {
Channel *amqp.Channel
}
func NewQueue() *Queue {
return &Queue{}
}
// Queue interface
type IQueue interface {
Connect(args ...interface{})
Publish(queue string, payload []byte) error
Listen(queue string) (<-chan amqp.Delivery, error)
Declare(queue string) (amqp.Queue, error)
}
// Connect - Connects to RabbitMQ
func (queue *Queue) Connect(args ...interface{}) {
var uri string
if args == nil {
// Get from env vars
uri = os.Getenv("RABBIT_MQ_URI")
if uri == "" {
log.Panic("No uri for queue given")
}
} else {
uri = args[0].(string)
}
// Make max 5 connection attempts, with a 1 second timeout
for i := 0; i < 5; i++ {
log.Println("Connecting to:", uri)
// If connection is successful, return new instance
conn, err := amqp.Dial(uri)
defer conn.Close()
if err == nil {
log.Println("Successfully connected to queue!")
channel, _ := conn.Channel()
queue.Channel = channel
return
}
log.Println("Failed to connect to queue, retrying...", err)
// Wait 1 second
time.Sleep(5 * time.Second)
}
}
// Declare a new queue
func (queue *Queue) Declare(queueName string) (amqp.Queue, error) {
return queue.Channel.QueueDeclare(
queueName,
true,
false,
false,
false,
nil,
)
}
// Publish a message
func (queue *Queue) Publish(queueName string, payload []byte) error {
return queue.Channel.Publish(
"",
queueName,
false,
false,
amqp.Publishing{
DeliveryMode: amqp.Persistent,
ContentType: "application/json",
Body: payload,
},
)
}
// Listen for a new message
func (queue *Queue) Listen(queueName string) (<-chan amqp.Delivery, error) {
return queue.Channel.Consume(
queueName,
"",
true,
false,
false,
false,
nil,
)
}
As you can see in the code above, I'm calling defer conn.Close() after making a connection, however, this immediately closes the connection again.
Here's a Go Playground spoofing what I'm talking about... https://play.golang.org/p/5cz2D4gDgn
The simple solution is to call conn.Close() from elsewhere. This might just be me, but I think it's kinda odd that you wouldn't expose the connection elsewhere, i.e. as a field in Queue. Exposing the ability to close the connection from the Queue would solve this and give you more flexibility.
So this:
type Queue struct {
// your original fields
Conn amqp.Connection
}
// Somewhere else
queue.Conn.Close()
You're other option is connecting, then doing all the actions you want with that connection, then closing. I'm thinking something like:
func action(conn amqp.Connection, args ...interface{}) (<-chan bool) {
done := make(chan bool)
go func(amqpConn amqp.Connection, dChan chan bool){
// Do what you want with the connection
dChan <- true
}(conn, done)
return done
}
func (queue *Queue) Connect(args ...interface{}) {
// your connection code
doneChans := make([](chan bool), 5)
for i := 0; i < 5; i++ {
conn, err := amqp.Dial(uri)
defer conn.Close()
if err != nil {
// handle error
}
done := action(conn)
}
// This for loop will block until the 5 action calls are done
for j := range doneChans {
isFinish := <-doneChans[j]
if !isFinish {
// handle bad state
}
}
}
One option is to have Connect return conn, and call defer conn.Close() in the caller.
package driver
// imports, etc
func (queue *Queue) Connect(args ...interface{}) amqp.Connection, error {
// ...
conn, err := amqp.Dial(uri)
if err != nil {
return nil, err
}
// ...
return conn, nil
}
Then in another package:
package stuff
// imports, etc
func doStuff() {
queue = driver.NewQueue()
conn, err := queue.Connect(args...)
if err != nil {
log.Fatalf("oh no! %v!", err)
}
defer conn.Close()
// Do stuff
}

Multiple connections to a TCP server

I've developed a small Go TCP server to make a chat application. But when I try to connect clients to it, the server works fine with two clients, but whenever I tried to connect the third client it is not connected to the server. I am running on Windows. What could be the issue?
package main
import (
"bufio"
"fmt"
"net"
)
var allClients map[*Client]int
type Client struct {
// incoming chan string
outgoing chan string
reader *bufio.Reader
writer *bufio.Writer
conn net.Conn
connection *Client
}
func (client *Client) Read() {
for {
line, err := client.reader.ReadString('\n')
if err == nil {
if client.connection != nil {
client.connection.outgoing <- line
}
fmt.Println(line)
} else {
break
}
}
client.conn.Close()
delete(allClients, client)
if client.connection != nil {
client.connection.connection = nil
}
client = nil
}
func (client *Client) Write() {
for data := range client.outgoing {
client.writer.WriteString(data)
client.writer.Flush()
}
}
func (client *Client) Listen() {
go client.Read()
go client.Write()
}
func NewClient(connection net.Conn) *Client {
writer := bufio.NewWriter(connection)
reader := bufio.NewReader(connection)
client := &Client{
// incoming: make(chan string),
outgoing: make(chan string),
conn: connection,
reader: reader,
writer: writer,
}
client.Listen()
return client
}
func main() {
allClients = make(map[*Client]int)
listener, _ := net.Listen("tcp", ":8080")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err.Error())
}
client := NewClient(conn)
for clientList, _ := range allClients {
if clientList.connection == nil {
client.connection = clientList
clientList.connection = client
fmt.Println("Connected")
}
}
allClients[client] = 1
fmt.Println(len(allClients))
}
}
Your code is fine. I compiled in on Linux, tried with 4 connections. Everything worked as expected.

Resources