Я устанавливаю tcp-сервер в проекте для домашних животных, который я пишу на ходу.Я хочу иметь возможность поддерживать часть всех подключенных клиентов, а затем изменять ее всякий раз, когда новый клиент подключается или отключается от моего сервера.
Мое главное умственное препятствие сейчас заключается в том, должен ли я объявлять уровень пакета.или просто передать фрагмент в мой обработчик.
Моей первой мыслью было объявить мой ClientList
фрагмент (я знаю, что фрагмент не может быть моим лучшим вариантом здесь, но я решилоставьте все как есть) как переменную уровня пакета.Хотя я думаю, что это сработает, я видел несколько постов, не одобряющих их использование.
Моя другая мысль состояла в том, чтобы объявить ClientList
в качестве фрагмента в моей основной функции, а затем я передал ClientList
к моей HandleClient
функции, поэтому, когда клиент подключается / отключается, я могу вызвать AddClient
или RemoveClient
, передать этот фрагмент и добавить / удалить соответствующего клиента.
Эта реализация рассматривается ниже.Определенно есть другие проблемы с кодом, но я застрял, пытаясь обернуть голову вокруг чего-то, что кажется очень простым.
type Client struct {
Name string
Conn net.Conn
}
type ClientList []*Client
// Identify is used to set the name of the client
func (cl *Client) Identify() error {
// code here to set the client's name in the based on input from client
}
// This is not a threadsafe way to do this - need to use mutex/channels
func (cList *ClientList) AddClient(cl *Client) {
*cList = append(*cList, cl)
}
func (cl *Client) HandleClient(cList *ClientList) {
defer cl.Conn.Close()
cList.AddClient(cl)
err := cl.Identify()
if err != nil {
log.Println(err)
return
}
for {
err := cl.Conn.SetDeadline(time.Now().Add(20 * time.Second))
if err != nil {
log.Println(err)
return
}
cl.Conn.Write([]byte("What command would you like to perform?\n"))
netData, err := bufio.NewReader(cl.Conn).ReadString('\n')
if err != nil {
log.Println(err)
return
}
cmd := strings.TrimSpace(string(netData))
if cmd == "Ping" {
cl.Ping() //sends a pong msg back to client
} else {
cl.Conn.Write([]byte("Unsupported command at this time\n"))
}
}
}
func main() {
arguments := os.Args
PORT := ":" + arguments[1]
l, err := net.Listen("tcp4", PORT)
if err != nil {
fmt.Println(err)
return
}
defer l.Close()
fmt.Println("Listening...")
// Create a new slice to store pointers to clients
var cList ClientList
for {
c, err := l.Accept()
if err != nil {
log.Println(err)
return
}
// Create client cl1
cl1 := Client{Conn: c}
// Go and handle the client
go cl1.HandleClient(&cList)
}
}
Из моего первоначального тестирования,это похоже на работу.Я могу распечатать свой список клиентов и вижу, что добавляются новые клиенты, и их имя добавляется после вызова Identify()
.
Когда я запускаю его с флагом -race, я получаю предупреждения о гонке данных, поэтому я знаю, что мне понадобится поточный способ обработки добавления клиентов.То же самое касается удаления клиентов, когда я добавляю это в.
Существуют ли какие-либо другие проблемы, которые я мог бы упустить, передав свой ClientList в HandleClient
, или какие-либо преимущества, которые я получил бы от объявления ClientList в качестве переменной уровня пакетавместо этого?