Следите за новинками
в нашем Telegram канале. Жми, чтобы подписаться:)
⊗goSpSoSC 253 of 280 menu
◀ ▶

Несколько клиентов TCP в Go

Один сервер может обслуживать много клиентов подряд. Если после принятия долго читать и писать в том же потоке, следующий клиент застрянет в очереди на порту, пока не освободится первый канал.

Поэтому после каждого успешного принятия запускают отдельную горутину с уже готовым соединением. Основной цикл снова вызывает ожидание клиента, а работа с предыдущим идет параллельно. Закрытие слушателя по-прежнему ставят в отложенный вызов при выходе из сервера.

Сервер в цикле и два клиента из main:

package main import ( "fmt" "log" "net" "time" ) func handleConn(conn net.Conn) { defer conn.Close() buf := make([]byte, 256) n, err := conn.Read(buf) if err != nil { log.Println(err) return } line := string(buf[:n]) out := "echo: " + line _, err = conn.Write([]byte(out)) if err != nil { log.Println(err) } } func multiServer(addr string) { ln, err := net.Listen("tcp", addr) if err != nil { log.Fatal(err) } defer ln.Close() for { conn, err := ln.Accept() if err != nil { log.Println(err) return } go handleConn(conn) } } func dialOnce(addr, msg string) { conn, err := net.Dial("tcp", addr) if err != nil { log.Fatal(err) } defer conn.Close() _, err = conn.Write([]byte(msg)) if err != nil { log.Fatal(err) } buf := make([]byte, 256) n, err := conn.Read(buf) if err != nil { log.Fatal(err) } fmt.Println(string(buf[:n])) } func main() { addr := "127.0.0.1:9090" go multiServer(addr) time.Sleep(10 * time.Millisecond) dialOnce(addr, "one") dialOnce(addr, "two") time.Sleep(50 * time.Millisecond) }

На экране две строки "echo: one" и "echo: two" в произвольном порядке. Каждый клиент получил ответ, не дожидаясь полного завершения другого.

Тот же цикл принятия без клиентов в файле, если проверку делаете отдельными запусками:

package main import ( "log" "net" ) func handleConn(conn net.Conn) { defer conn.Close() buf := make([]byte, 256) _, err := conn.Read(buf) if err != nil { log.Println(err) } } func main() { addr := "127.0.0.1:9090" ln, err := net.Listen("tcp", addr) if err != nil { log.Fatal(err) } defer ln.Close() for { conn, err := ln.Accept() if err != nil { log.Fatal(err) } go handleConn(conn) } }

Дана функция обработки:

func handleConn(conn net.Conn) { defer conn.Close() buf := make([]byte, 256) n, _ := conn.Read(buf) conn.Write([]byte("ok:" + string(buf[:n]))) }

Оформите бесконечный цикл принятия на "127.0.0.1:9090" и запускайте handleConn отдельной горутиной для каждого conn.

Дан следующий фрагмент сервера:

for { conn, err := ln.Accept() if err != nil { log.Fatal(err) } handleConn(conn) }

Скажите, чем такой вариант хуже для второго клиента, пока первый еще читает данные.

Скажите, где в обработчике соединения нужно закрывать conn, если принятие запускает много горутин.

← →
↑
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить