go语言Socket

Socket编程基本原理

Socket编程是指在网络编程中使用Socket接口进行应用程序开发的过程。Socket(套接字)是网络通信中的一个基本概念,是一个抽象的数据结构,用于描述网络中的一个端点。Socket包含了IP地址、端口号、协议类型等信息,用于标识网络中的一个特定的进程或设备。

Socket编程基于两个概念:客户端和服务器。客户端是指发起请求的一方,服务器是指提供服务的一方。通过在网络上建立连接,客户端可以向服务器发送请求,服务器则处理请求并将响应返回给客户端。

Socket编程需要使用一种特殊的协议,这种协议包含了一系列规定的数据格式和交互方式。目前主流的Socket协议有TCP和UDP。TCP协议是一种面向连接的协议,提供数据传输的可靠性。UDP协议则是一种无连接的协议,数据传输不可靠但速度快。

  • TCP(传输控制协议)​:面向连接,可靠,有序,基于字节流。
  • UDP(用户数据报协议)​:无连接,不可靠,无序,基于数据报。

Go 语言提供了强大的 net 包,简化了 Socket 编程,使得开发者无需直接操作底层的系统调用即可实现网络通信。


TCP Socket 编程

TCP 是一种面向连接的协议,适用于需要可靠数据传输的场景。

服务器端实现

以下是一个简单的 TCP 服务器示例,它监听指定端口,接受客户端连接,并回显接收到的消息。

go 复制代码
package main

import (
    "bufio"
    "fmt"
    "net"
)

func handleConnection(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    for {
        // 读取客户端发送的数据
        message, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("读取数据错误:", err)
            break
        }
        fmt.Printf("收到消息: %s", message)
        // 回显消息给客户端
        conn.Write([]byte("Echo: " + message))
    }
}

func main() {
    // 监听本地 8080 端口
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("监听端口失败:", err)
        return
    }
    defer listener.Close()
    fmt.Println("服务器正在监听端口 8080...")

    for {
        // 接受客户端连接
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("接受连接失败:", err)
            continue
        }
        // 处理连接(可以并发处理多个连接)
        go handleConnection(conn)
    }
}

说明

  1. 使用 net.Listen 监听指定地址和端口。
  2. 使用 listener.Accept 接受客户端连接,返回一个 net.Conn 对象。
  3. 为每个连接启动一个新的 goroutine 处理,实现并发处理多个客户端。

客户端实现

以下是一个简单的 TCP 客户端示例,它连接到服务器并发送消息。

go 复制代码
package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func main() {
    // 连接到服务器(localhost:8080)
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        fmt.Println("连接服务器失败:", err)
        return
    }
    defer conn.Close()

    reader := bufio.NewReader(os.Stdin)
    for {
        // 从标准输入读取用户输入
        fmt.Print("请输入消息: ")
        message, _ := reader.ReadString('\n')
        // 发送消息到服务器
        _, err := conn.Write([]byte(message))
        if err != nil {
            fmt.Println("发送消息失败:", err)
            return
        }
        // 接收服务器回显的消息
        response, err := bufio.NewReader(conn).ReadString('\n')
        if err != nil {
            fmt.Println("接收消息失败:", err)
            return
        }
        fmt.Print("服务器回显: " + response)
    }
}

说明

  1. 使用 net.Dial 连接到服务器。
  2. 从标准输入读取用户输入并发送到服务器。
  3. 接收服务器的回显消息并显示。

UDP Socket 编程

UDP 是一种无连接的协议,适用于对实时性要求高但对可靠性要求不高的场景。下面将分别介绍如何使用 Go 实现 UDP 服务器和客户端。

服务器端实现

以下是一个简单的 UDP 服务器示例,它监听指定端口,接收客户端发送的数据,并回显消息。

go 复制代码
package main

import (
    "fmt"
    "net"
)

func main() {
    // 监听本地 8081 端口的 UDP
    addr, err := net.ResolveUDPAddr("udp", ":8081")
    if err != nil {
        fmt.Println("解析地址失败:", err)
        return
    }

    conn, err := net.ListenUDP("udp", addr)
    if err != nil {
        fmt.Println("监听 UDP 失败:", err)
        return
    }
    defer conn.Close()
    fmt.Println("UDP 服务器正在监听端口 8081...")

    buffer := make([]byte, 1024)
    for {
        // 读取客户端发送的数据
        n, clientAddr, err := conn.ReadFromUDP(buffer)
        if err != nil {
            fmt.Println("读取数据错误:", err)
            continue
        }
        message := string(buffer[:n])
        fmt.Printf("收到来自 %s 的消息: %s", clientAddr, message)

        // 回显消息给客户端
        _, err = conn.WriteToUDP([]byte("Echo: "+message), clientAddr)
        if err != nil {
            fmt.Println("发送消息失败:", err)
        }
    }
}

说明

  1. 使用 net.ResolveUDPAddr 解析 UDP 地址。
  2. 使用 net.ListenUDP 监听 UDP 端口。
  3. 使用 ReadFromUDP 接收数据,并获取客户端地址。
  4. 使用 WriteToUDP 发送数据回客户端。

客户端实现

以下是一个简单的 UDP 客户端示例,它发送消息到服务器并接收回显。

go 复制代码
package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func main() {
    // 解析服务器地址
    serverAddr, err := net.ResolveUDPAddr("udp", "localhost:8081")
    if err != nil {
        fmt.Println("解析服务器地址失败:", err)
        return
    }

    // 创建一个 UDP 连接(实际上是无连接的)
    conn, err := net.DialUDP("udp", nil, serverAddr)
    if err != nil {
        fmt.Println("连接 UDP 服务器失败:", err)
        return
    }
    defer conn.Close()

    reader := bufio.NewReader(os.Stdin)
    for {
        // 从标准输入读取用户输入
        fmt.Print("请输入消息: ")
        message, _ := reader.ReadString('\n')
        // 发送消息到服务器
        _, err := conn.Write([]byte(message))
        if err != nil {
            fmt.Println("发送消息失败:", err)
            return
        }
        // 接收服务器回显的消息
        buffer := make([]byte, 1024)
        n, err := conn.Read(buffer)
        if err != nil {
            fmt.Println("接收消息失败:", err)
            return
        }
        fmt.Print("服务器回显: " + string(buffer[:n]))
    }
}

说明

  1. 使用 net.ResolveUDPAddr 解析服务器地址。
  2. 使用 net.DialUDP 创建一个 UDP 连接(实际上是无连接的)。
  3. 发送和接收数据类似于 TCP 客户端。

相关推荐
名字还没想好☜9 小时前
Go 时间格式化为什么用 2006-01-02:time.Format/Parse 的参考时间、时区与解析踩坑
开发语言·后端·golang·go
我的div丢了肿么办1 天前
go语言中的map,map定义不同的数据类型,循环map,map的无序性
后端·go
名字还没想好☜1 天前
Go 1.21 context.WithoutCancel 实战:父 context 取消了,收尾任务还要继续跑
开发语言·后端·golang·go
newerp1 天前
Golang 接口的两副面孔:eface、iface 与动态派发之谜
后端·程序员·go
明月_清风1 天前
递归算法:从原理到实战,一次讲透
后端·算法·go
运维开发笔记1 天前
9.1 Go Interface 基础知识学习笔记
go
a187927218311 天前
【算法】回溯算法(一):从一道 IP 题到万能模板
算法·leetcode·go·回溯·leetcode93·ip复原·算法讲解
名字还没想好☜1 天前
Go 用 -race 抓数据竞争:一个偶发崩溃的排查、原理与修复
开发语言·后端·golang·go
Vespeng2 天前
打破传统 MVC:在 Go 中实践高内聚的业务驱动架构
架构·go·gin
用户029669769822 天前
摸着石头过河:我用Go把微信加密模块给逆向了一回
微信·go