В прошлой теме было рассмотрено создание простейшего сервера TCP, к которому подключается клиент, и этому клиенту отправляется некоторое сообщение. Теперь рассмотрим, как клиент может посылать сообщения и получать ответ.
Для начала определим следующий сервер:
package main import ( "fmt" "net" ) var dict = map[string]string{ "red": "красный", "green": "зеленый", "blue": "синий", "yellow": "желтый", } func main() { listener, err := net.Listen("tcp", ":4545") if err != nil { fmt.Println(err) return } defer listener.Close() fmt.Println("Server is listening...") for { conn, err := listener.Accept() if err != nil { fmt.Println(err) conn.Close() continue } go handleConnection(conn) // запускаем горутину для обработки запроса } } // обработка подключения func handleConnection(conn net.Conn) { defer conn.Close() for { // считываем полученные в запросе данные input := make([]byte, (1024 * 4)) n, err := conn.Read(input) if n == 0 || err != nil { fmt.Println("Read error:", err) break } source := string(input[0:n]) // на основании полученных данных получаем из словаря перевод target, ok := dict[source] if ok == false{ // если данные не найдены в словаре target = "undefined" } // выводим на консоль сервера диагностическую информацию fmt.Println(source, "-", target) // отправляем данные клиенту conn.Write([]byte(target)) } }
Сервер имитирует поведение программы для перевода слов. Для этого определен словарь dit, который содержит англоязычные слова и их перевод.
В бесконечном цикле сервер принимает подключения. Однако вместо прямой обработки подключения сервер запускает горутину в виде функции handleConnection, в которой и обрабатывается подключение. Это позволит входящим клиентам не ждать, пока первый из них будет обработан. Таким образом, все входящие клиенты в определенной степени будут обрабатываться одновременно.
В функции handleConnection получаем запрос от клиента. Для этого выделяем буфер достаточной длины в 4096 байт.
input := make([]byte, (1024 * 4)) n, err := conn.Read(input)
В данном случае мы ожидаем, что запрос от клиента не превысит 4096 байт, однако точный размер запроса и его максимальный размер не всегда бывают известны. В этом случае мы можем применять различные техники, в частности, в бесконечном цикле считывать данные запроса от клиента и только потом их обрабатывать. Но в данном случае мы разберем более простую ситуацию.
Получив запрос, преобразовав его строку, получаем значение из словаря и отправляем его обратно клиенту:
conn.Write([]byte(target))
Для взаимодействия с этми сервером определим следующий клиент:
package main import ( "fmt" "net" ) func main() { conn, err := net.Dial("tcp", "127.0.0.1:4545") if err != nil { fmt.Println(err) return } defer conn.Close() for{ var source string fmt.Print("Введите слово: ") _, err := fmt.Scanln(&source) if err != nil { fmt.Println("Некорректный ввод", err) continue } // отправляем сообщение серверу if n, err := conn.Write([]byte(source)); n == 0 || err != nil { fmt.Println(err) return } // получем ответ fmt.Print("Перевод:") buff := make([]byte, 1024) n, err := conn.Read(buff) if err !=nil{ break} fmt.Print(string(buff[0:n])) fmt.Println() } }
На клиенте в бесконечном цикле вводим слово для перевода и отправляем серверу сообщение:
if n, err := conn.Write([]byte(source));
И затем получаем от сервера ответ и выводим его на консоль. Так как ответ от сервера может быть переменной длины, то для получения ответа в бесконечом цикле считываем данные с помощью метода Read:
buff := make([]byte, 1024) n, err := conn.Read(buff) if err !=nil{ break} fmt.Print(string(buff[0:n]))
Запустим сервер.
Затем запустим программу клиента и введем какое-либо значение и сервер возвратит перевод слова:
Для выхода из программ сервера и клиента необходимо нажать комбинацию клавиш Ctrl+C.