Несколько клиентов 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, если
принятие запускает много горутин.