Ответ
Да, я работал с низкоуровневыми сетевыми протоколами в Go. Основным инструментом для этого является стандартный пакет net
.
Пакет net
предоставляет абстракции для работы с TCP и UDP сокетами, позволяя создавать как серверные, так и клиентские приложения.
- TCP (Transmission Control Protocol): Протокол с установкой соединения, гарантирующий доставку и порядок пакетов. Используется для большинства веб-сервисов (HTTP, FTP), баз данных и т.д.
- UDP (User Datagram Protocol): Протокол без установки соединения, не гарантирует доставку и порядок. Используется там, где важна скорость, а не надежность (DNS, VoIP, онлайн-игры).
Пример TCP-сервера
Сервер слушает входящие соединения на определенном порту. Каждое новое соединение обрабатывается в отдельной горутине для обеспечения конкурентности.
func handleConnection(conn net.Conn) {
defer conn.Close()
fmt.Printf("New connection from %sn", conn.RemoteAddr())
// Буфер для чтения данных
buffer := make([]byte, 1024)
for {
n, err := conn.Read(buffer)
if err != nil {
if err != io.EOF {
fmt.Println("Read error:", err)
}
break // Клиент закрыл соединение
}
fmt.Printf("Received: %s", string(buffer[:n]))
// Отправляем ответ (эхо)
conn.Write([]byte("Message received.n"))
}
fmt.Printf("Connection from %s closedn", conn.RemoteAddr())
}
func main() {
// Начинаем слушать порт 8080 по протоколу TCP
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal("Listen error:", err)
}
defer listener.Close()
fmt.Println("Server is listening on port 8080")
for {
// Ожидаем нового подключения
conn, err := listener.Accept()
if err != nil {
fmt.Println("Accept error:", err)
continue
}
// Запускаем обработчик в новой горутине
go handleConnection(conn)
}
}
Пример TCP-клиента
Клиент устанавливает соединение с сервером и отправляет ему данные.
func main() {
// Устанавливаем соединение с сервером
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal("Dial error:", err)
}
defer conn.Close()
// Отправляем сообщение
_, err = conn.Write([]byte("Hello from client!"))
if err != nil {
log.Println("Write error:", err)
}
// Читаем ответ от сервера
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
log.Println("Read error:", err)
}
fmt.Printf("Server response: %s", string(buffer[:n]))
}
Ключевые аспекты при работе с сокетами:
- Управление соединениями: Всегда закрывайте соединение с помощью
defer conn.Close()
. - Обработка ошибок:
conn.Read()
возвращаетio.EOF
, когда клиент закрывает соединение. Это штатная ситуация, а не ошибка. - Таймауты: Для предотвращения "зависания" на операциях чтения/записи используются таймауты:
conn.SetDeadline()
,conn.SetReadDeadline()
,conn.SetWriteDeadline()
.