Background
I'm creating a chatroom server using Golang. Every time a client connects to the server, I'm starting the Client function as a new thread, so that each client gets a thread that is listened to. I was able to create a simple client server connection based on this tutorial, but now I am trying to create connections for multiple clients so that they can send messages to chatrooms.
Code explanation
By following this tutorial, it looks like I can create a thread by using go func()
and wait for a connection to be made by including a channel (<-newClient
). I pass a bool value into the channel before calling the user function, because the user function will run forever and I want to make other client connections. Each client connection will have the user function running for it.
Problem
I don't know how to pass the user connection variable to my other functions. I'm placing conn net
in the arguments for my functions, but this is just a placeholder because I'm not sure the proper way to do it.
Also, my go func()
with the call to user() after the channel implementation is my best attempt at multithreading, but I'm not sure that I'm thinking about it right.
Server.go
package main
import (
"net"
"fmt"
"bufio"
"strings"
"container/list"
"time"
)
type chatRoom struct {
name string
messages list.List
users list.List
lastUsed time.Time
}
var chatRooms *list.List //A list of chatrooms where each client can post messages, where messages can be seen by all clients in the chatroom
var conn net.Conn
func user(conn net) {
for {
message, _ := bufio.NewReader(conn).ReadString('
') // will listen for message to process ending in newline (
)
fmt.Print("Message Received:", string(message)) // output message received
s := strings.Split(string(message), ":")
if strings.Compare(s[0],"create") == 0{ //create a chat room
create(conn, s[1])
}else if strings.Compare(s[0],"list") == 0 { //List the current chatrooms
msg = listRooms(conn)
}else if strings.Compare(s[0],"join") == 0 { //Join the user to a chat room
join(conn, s[1])
}else if strings.Compare(s[0],"leave") == 0 { //Remove the user from a chatroom
leave(conn, s[1])
}else if strings.Compare(s[0],"message") == 0{ //Send a message to a chatroom
message(conn, s[1], s[2])
}
}
}
func main() {
fmt.Println("Launching server...")
this.userList = list.New()
this.chatRooms = list.New();
ln, _ := net.Listen("tcp", ":8081") // listen on all interfaces
conn, _ := ln.Accept() // accept connection on port
for { // run loop forever (or until ctrl-c)
go func(){
newClient := make(chan bool)
ln, _ := net.Listen("tcp", ":8081") // listen on all interfaces
conn, _ := ln.Accept() // accept connection on port
newClient <- true
user(conn)
}
<-newClient
}
}
It is very important to understand a distinction here: this is concurrency, not multithreading, and these are goroutines, not threads. These concepts are not interchangeable. As to your core issue, there are some significant issues with your implementation:
conn
. Every time you accept a connection, you overwrite the conn
which is shared by every goroutine. You should instead pass the conn
to your goroutine so it has its own local copy, or create a new conn
variable in each loop iteration instead of re-using one.ln.Accept
on the existing listener to continue accepting new connections. Take a look at the introduction of the documentation for the net
package, or check any code that uses listeners in Go for an example.newClient
inside the goroutine, then trying to reference it outside the goroutine. This won't even compile, and it's unclear what you are trying to do with this channel in the first place.Take a look at some existing networking code - in the net
or net/http
libraries or some popular projects on GitHub - to see good examples of how to write a network application. Do some web searches for blog posts or tutorials or how-to's, there are tons out there. And definitely read the documentation for the packages you're using, it will help you a lot.