Websockets предоставляют удобный способ установления постоянного соединения между клиентом и сервером, что делает их отличным выбором для реализации чата. В этой статье я покажу, как создать простой чат на языке программирования Golang, используя пакет gorilla/websocket
. Под "простым" чатом будет подразумеваться только функционал получения и отправки сообщений. Для
Первым шагом будет установка библиотеки, которая поможет нам работать с websockets в Golang. Установим ранее упомянутый пакет с помощью следующей команды: go get github.com/gorilla/websocket
Работа сервера будет следующей: клиент отправляет запрос на рукопожатие серверу, сервер обновляет необработанное HTTP-соединение до соединения на основе веб-сокета, далее соединение добавляется в слайс :)). Далее начинается цикл событий: при получении сообщения от клиента сервер проходится по всем остальным соединениям и отправляет им данное сообщение, а клиент его обрабатывает.
Сервер
Создадим обработчик для запроса:
func main() {
http.HandleFunc("/start", handlers.SocketHandler)
log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
Далее создадим экземпляр структуры Upgrader (он нужен для обновления HTTP соединения до WebSocket соединения), слайс соединений и связная с ним функция для удаления соединения:
var upgrader = websocket.Upgrader{} // оставим без изменений
var connections = []*websocket.Conn{}
func removeConn(slice []*websocket.Conn, val *websocket.Conn) []*websocket.Conn {
index := -1
for i, v := range slice {
if v == val {
index = i
break
}
}
if index != -1 {
if index < len(slice)-1 {
copy(slice[index:], slice[index+1:])
}
slice = slice[:len(slice)-1]
}
return slice
}
Далее напишем простую функцию для обработки запроса на рукопожатие, включающую в себя бесконечный цикл получения сообщений от пользователя:
func SocketHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("Error during connection upgradation:", err)
return
}
defer conn.Close()
connections = append(connections, conn)
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
connections = removeConn(connections, conn)
log.Println("Error during message reading:", err)
break
}
log.Printf("Server: %s", message)
for _, c := range connections {
if c != conn {
err = c.WriteMessage(messageType, message[:len(message)-1])
if err != nil {
connections = removeConn(connections, conn)
log.Println("Error during message writing:", err)
break
}
}
}
}
connections = removeConn(connections, conn)
}
Клиент
var done chan interface{}
func main() {
done = make(chan interface{})
socketUrl := "ws://localhost:8080" + "/start"
conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
if err != nil {
log.Fatal("Error connecting to websocket server:", err)
}
defer conn.Close()
internal.ClearScreen()
internal.RunChat(conn, done) // К ней вернемся чуть позже
}
Функция для ввода сообщения пользователем в консоль представлена ниже.
func userInput(inputChan chan string) {
reader := bufio.NewReader(os.Stdin)
msg, _ := reader.ReadString('\n')
inputChan <- msg
}
Она используется в функции, которая отправляет данного сообщения на сервер.
func chatInputHandler(conn *websocket.Conn, done chan interface{}) {
for {
inputChan := make(chan string)
go userInput(inputChan)
select {
case input := <-inputChan:
if strings.TrimSpace(input) == "" {
continue
}
err := conn.WriteMessage(websocket.TextMessage, []byte(input))
if err != nil {
log.Println("Error during writing to websocket:", err)
return
}
PrintColoredText("message has been sent", "\033[32m")
case <-interrupt:
log.Println("Interrupt signal received. Closing all pending connections")
err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("Error during closing websocket:", err)
return
}
select {
case <-done:
log.Println("Receiver Channel Closed")
case <-time.After(time.Duration(1) * time.Second):
log.Println("Timeout in closing receiving channel")
}
return
}
}
}
Теперь посмотрим, как работает написанный код. Запустим сервер и несколько клиентов.


Хочется верить, что данный пример кому-то сможет помочь.