vpdhjjk
@vpdhjjk

Как реализовать обмен сообщениями между двумя пользователями на Golang?

Видел много примеров с использованием websocket, где сообщения отправлялись в общий чат, либо создавались "комнаты". Как создать обмен сообщениями между двумя пользователями по айди или нику, например? Какие пакеты посоветуете использовать?
  • Вопрос задан
  • 717 просмотров
Пригласить эксперта
Ответы на вопрос 3
@yayashitoya
WebSocket, это способ обойти ограничения http для браузеров.
Когда у вас обе программы на чистом Go - нет необходимости использования костылей WebSocket.
Используйте непосредственно https://golang.org/pkg/net/

"По ID или нику" - это вообще из другой оперы.
Если ID/ник можно свободно менять - то какая разница что использовать? Вам реально нужен пакет, чтобы передать с одного компьютера на другой простую строку?

Если ID/ник жестко ограничены, то нужен некий сервер, которому доверяют оба ваших клиента, и который и проводит аутентификацию.
Ответ написан
Комментировать
mhthnz
@mhthnz
PHP, YII2, Golang, Linux
Создаете структуру Clients. в ней поля nickname, connect.
type Client struct {
       ID        int                  // Идентификатор юзера
       Nickname   string             // Никнейм клиента
       Conn 	 *websocket.Conn    // Сокет клиента
}

После авторизации клиента на вашем сокет сервере, добавляете его в глобальный массив clients, ну и собственно ловите пакеты сообщений, например в json:
{"to" : "nickname", "message" : "hello"}
Дальше циклом пробегаетесь по массиву clients и если ник клиента совпадает с полем to из json, то отправляете ему сообщение.
P. S. Самое поверхностное описание, без углубления в каналы юзера/сессии/аутентификацию/работы с json
Ответ написан
@alex__brin
Программист-безопасник
Давайте представим ситуацию. У нас есть некий очень простой протокол, а передавать все мы будем с помощью JSON

Привел в качестве примера server.go и client.go
server.go содержит в себе примерно то, что должно быть в обычном сервере
client.go просто для демонстрации подключения (хотя можно подключаться и через telnet)
Надеюсь, получилось не слишком сложно и вполне понятное
Если останутся вопросы или нужна будет помощь -- можете писать в личные сообщения ВКонтакте, ссылочка в профиле, что будет более оперативно (либо тут в комментариях) :)

server.go
package main

import (
	"io"
	"strings"
	"encoding/json"
	"bufio"
	"time"
	"log"
	"net"
)

// Это наш сервер
type Server struct {
	Addr string
	IdleTimeout time.Duration
	MaxReadBytes int64
}
func (s Server) ListenAndServe() error {
	if s.Addr == "" {
		s.Addr = ":3000"
	}

	log.Printf("starting server on %v\n", s.Addr)

	listener, err := net.Listen("tcp", s.Addr)
	if err != nil {
		return err
	}
	defer listener.Close()

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Printf("error accption connection %v", err)
			continue
		}

		connection := &Conn{
			Conn: conn,
			IdleTimeout: s.IdleTimeout,
			MaxReadBytes: s.MaxReadBytes,
		}
		connection.SetDeadline(time.Now().Add(connection.IdleTimeout))

		log.Printf("accepted connection from %v", conn.RemoteAddr())

		connection.Write([]byte("Content-Type: appliaction/json\n  action:\n    ping - ping\n    scream - upper the text\n  body - content\n"))
		go handle(connection) // запускаем обработчик в новой горутине, чтобы быть готовым принять еще одно соединение 
	}
}

type Conn struct {
	net.Conn
	IdleTimeout time.Duration // Ожидание, когда сервер что-нибудь скажет или прочитает
	MaxReadBytes int64 // максимальный объем передаваемых данных, чтобы клиент вдруг не захотел передать нам гигабайты данных
}
func (c *Conn) Write(p []byte) (int, error) {
	c.updateDeadline()
	return c.Conn.Write(p)
}
func (c *Conn) Read(b []byte) (int, error) {
	c.updateDeadline()
	r := io.LimitReader(c.Conn, c.MaxReadBytes)
	return r.Read(b)
}
func (c *Conn) updateDeadline() {
	c.Conn.SetDeadline(time.Now().Add(c.IdleTimeout))
}

// Это будет наш запрос
type Request struct {
	Action string `json:"action"` // наше действие
	Body   string `json:"body"` // и само тело запроса
}

func handle(conn *Conn) {
	defer func() { // Функция выполнится после return этой функции
		log.Printf("closing connection from %v", conn.RemoteAddr())
		conn.Close()
	}()

	r := bufio.NewReader(conn.Conn)
	w := bufio.NewWriter(conn.Conn)
	scaner := bufio.NewScanner(r)
	for {
		if !scaner.Scan() { // если произошло что-то неладное
			if err := scaner.Err(); err != nil {
				log.Printf("%v(%v)", err, conn.RemoteAddr())
				return // то выйдем из функции (и, благодаря defer, закроем соединение)
			}
		}

		req := Request{}
		scanned := scaner.Text() // читаем текст из сети
		log.Println(scanned)
		err := json.Unmarshal([]byte(scanned), &req) // парсим json в нашу структуру
		if err != nil {
			log.Printf("error parsing json: ", err)
		}

		log.Println("New request. Action:", req.Action, "|", "Message:", req.Body)

		response := ""

		switch req.Action {
		case "ping": // будем отвечать pong
			response = "pong"

		case "scream": // кричать
			response = strings.ToUpper(req.Body)

		case "bye": // и прощаться
			response = "Bye!"

		default: // иначе будем ругаться, что не знаем такого действия
			response = "Unknown Action"
		}

		w.WriteString(response + "\n")
		w.Flush()

		if req.Action == "bye" { // если клиент попрощался, то закрываем соединение
			return
		}
	}
}

func main() {
	srv := Server{":3030", time.Minute, 1024}
	srv.ListenAndServe()
}
client.go
package main

import (
	"encoding/json"
	"os"
	"fmt"
	"net"
)

type Request struct {
	Action string `json:"action"`
	Body   string `json:"body"`
}

func main() {
	conn, err := net.Dial("tcp", "localhost:3030") // Подключаемся к порту
	checkErr(err)
	defer conn.Close()

	hello := make([]byte, 1024)
	conn.Read(hello)
	fmt.Println(string(hello))

	req := Request{}
	for {
		fmt.Print("Action: ")
		fmt.Fscan(os.Stdin, &req.Action)
		fmt.Print("Body: ")
		fmt.Fscan(os.Stdin, &req.Body)

		jsonData, err := json.Marshal(req)
		if err != nil {
			fmt.Println(err)
			continue
		}
		jsonData = append(jsonData, byte('\n'))
		fmt.Println(string(jsonData))
		i, err := conn.Write(jsonData)
		if err != nil {
			panic(err)
		}
		fmt.Println("write", i, "bytes")

		response := make([]byte, 1024)
		i, err = conn.Read(response)
		if err != nil {
			panic(err)
		}
		fmt.Println("read", i, "bytes")
		fmt.Println(string(response))
	}

}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы