Go Socket编程

Socket是计算机网络中用于在节点内发送或接收数据的内部端点.具体来说.是网络软件(协议栈)中端点的一种表示.包含通信协议 目标地址 状态等.是系统资源的一种形式.它在网络中所处的位置大致就是如图所示的Socket API层.位于应用层与传输层之间.其中的传输层就是TCP/IP所在的地方.开发人员平时通过代码编写的应用程序大多属于应用层范畴.

如图所示.SocketAPI起到的就是连接应用层与传输层的作用.

Socket API的诞生就是为了应用程序能更方便的将数据经由传输层来传输.所以它本质上是对TCP/IP的运用进行了一层封装.然后应用程序直接调用SocketAPI接口进行通信.

总结来说.Socket是进程间数据传输的媒介.为了保证连接的可靠性.需要特别注意建立连接和关闭连接的过程.为了确保准确 完整的传输数据.客户端和服务端会来回进行了多次网络通信才完成连接的创建和关闭.这也是在运用一个连接时所花费的额外成本.

TCP Socket:

Go语言中进行网络编程.相比传统的网络编程更加简洁.Go语言提供了net包来处理Socket.net包对Socket连接过程进行了抽象和封装.无论使用什么协议建立了什么形式的连接.都只需要net.Dial()函数即可.

定义如下.

func Dial(net,addr string) (Conn,error)

net参数是网络协议的名字.addr参数是IP地址或域名.而端口号以":"的形式跟随在地址或域名的后面.端口号可选.如果连接成功.则返回连接对象.否则返回error.

除Dial()函数外.还有一个名为DialTCP()的函数用来建立TCP连接.

定义如下.

func DialTCP(network string,laddr,raddr *TCPAddr) (*TCPConn,error)

network参数可以是tcp或者tcp4或者tcp6.laddr为本地地址.通常为nil.raddr为目的地址.为TCPAddr类型的指针.该函数返回一个*TCPConn对象.可通过Read()和Write()方法传递数据.

Dial()函数的几种常见协议调用方式如下:

TCP连接:

TCP连接直接通过net.Dial("tcp","ip:port")的形式调用.代码如下.

conn,err := net.Dial("tcp","192.168.0.1:8087")

UDP连接:

UDP连接直接通过net.Dial("udp","ip:port")的形式调用.代码如下.

net.Dial("udp","192.168.0.2:8088")

目前.Dial()函数支持如下几种网络协议.tcp tcp4(仅限IPv4) tcp6(仅限IPv6) udp udp4(仅限IPv4) udp6(仅限IPv6) ip ip4(仅限IPv4)和ip6(仅限IPv6).

在成功建立连接后.就可以进行数据的发送和接收.使用Write()方法发送数据.使用Read方法接收数据.

UDP Socket:

由于UDP是无连接的.所以服务器端只需要指定IP地址和端口号.然后监听该地址.等待客户端与之建立连接.即可通信.

在Go语言中创建UDP Socket.可以通过如下函数或方法来实现.

1).创建监听地址

创建监听地址使用ResolveUDPAddr()函数.定义如下.

func ResolveUDPAddr(network,address string) (*UDPAddr,error)

2).创建监听连接

创建监听连接使用ListenUDP()函数.定义如下.

func ListenUDP(network string,laddr UDPAddr) (UDPConn,error)

3).接收UDP数据

接收UDP数据使用ReadFromUDP()方法.定义如下.

func(c *UDPConn) ReadFromUDP(b []byte) (int,*UDPAddr,error)

4).写入数据到UDP

func(c *UDPConn) WriteToUDP (b []byte,addr *UDPAddr) (int,error)

Socket简易聊天:

服务端每收到一个连接.就让一个goroutine去通过Socket和客户端进行交流.goroutine执行流程.先接收信息.在发送信息.客户端从终端获取输入.发送给指定的IP地址和端口服务器.接收服务信息.

TCP Socket服务端:

Go 复制代码
package main

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

func main() {
    listenSock, err := net.Listen("tcp", "127.0.0.1:8080")
    if err != nil {
       Log("监听失败", err)
    }

    for {
       connectSock, err := listenSock.Accept()
       defer connectSock.Close()
       if err != nil {
          Log("接收请求失败", err)
       }
    }
}

func Work(sock net.Conn) {
    for {
       var buf [1024]byte
       n, err := sock.Read(buf[:])
       if err != nil {
          Log("读取数据出错", err)
       }
       fmt.Printf("%v 说: %v", sock.RemoteAddr().String(), string(buf[:n]))

       var str string
       reader := bufio.NewReader(os.Stdin)
       str, err = reader.ReadString('\n')
       if err != nil {
          Log("发送失败", err)
       }
       sock.Write([]byte(str))
    }
}

func Log(msg string, err error) {
    fmt.Println(msg, err)
    os.Exit(-1)
}

TCP Socket客户端:

Go 复制代码
package main

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

func main() {
    server, err := net.Dial("tcp", "127.0.0.1:8080")
    if err != nil {
       fmt.Println("拨号失败:", err)
       os.Exit(-1)
    }
    defer server.Close()

    for {
       var str string
       var buf [1024]byte
       reader := bufio.NewReader(os.Stdin)
       str, err := reader.ReadString('\n')
       if err != nil {
          fmt.Println("发送失败:", err)
          os.Exit(-1)
       }
       server.Write([]byte(str))
       var n int
       n, err = server.Read(buf[:])
       if err != nil {
          fmt.Println("读取失败:", err)
          os.Exit(-1)
       }
       fmt.Printf("%v 说: %v", server.RemoteAddr().String(), string(buf[:n]))
    }
}

UDP Socket服务端:

UDP服务器无连接.接收到消息.将消息输出.在回复收到字样后.服务器处于死循环中.只要客户端拨号的IP和端口是UDP服务器的IP和端口.该UDP服务器都可以接收.也不需要额外的goroutine来处理多个客户端的信息.

Go 复制代码
func main() {
    conn, err := net.ListenUDP("udp", &net.UDPAddr{
       IP:   net.ParseIP("127.0.0.1"),
       Port: 8888,
    })
    if err != nil {
       fmt.Println("ListenUDP error:", err)
       os.Exit(-1)
    }
    defer conn.Close()

    for {
       var buf [1024]byte
       n, addr, err := conn.ReadFromUDP(buf[:])
       if err != nil {
          fmt.Println("接收消息出错:", err)
       }
       fmt.Println(addr.String(), string(buf[:n]))
       conn.WriteToUDP([]byte("收到"), addr)
    }
}

UDP Socket客户端:

Go 复制代码
package main

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

func main() {
    con, err := net.DialUDP("udp", nil, &net.UDPAddr{
       IP:   net.ParseIP("127.0.0.1"),
       Port: 8888,
    })
    if err != nil {
       fmt.Println("拨号失败", err)
    }
    for {
       reader := bufio.NewReader(os.Stdin)
       str, _ := reader.ReadString('\n')
       str = strings.Trim(str, "\r\n")
       if strings.ToUpper(str) == "STOP" {
          fmt.Println("退出")
          return
       }
       con.Write([]byte(str))
       var buf [1024]byte
       n, _, _ := con.ReadFromUDP(buf[:])
       fmt.Println(con.RemoteAddr().String(), string(buf[:n]))
    }
}

语雀地址https://www.yuque.com/itbosunmianyi/xg8vfe?

《Go.》 密码:xbkk 欢迎大家访问.提意见.

相关推荐
feifeigo1231 小时前
基于无迹变换的电网概率潮流分析 MATLAB 实现
开发语言·算法·matlab
ffqws_1 小时前
Spring Boot 接收前端请求的四种参数方式
前端·spring boot·后端
时空系2 小时前
第13篇:综合实战——制作我的小游戏 Rust中文编程
开发语言·后端·rust
咸鱼咸鱼2 小时前
RustDesk 自建服务端教程:开源远程桌面,完全掌控你的数据
后端
CoderCodingNo2 小时前
【信奥业余科普】C++ 的奇妙之旅 | 19:内存的门牌号——地址与指针的设计原理
开发语言·c++
0xDevNull2 小时前
JDK多版本切换安装与配置
java·后端
@insist1232 小时前
信息安全工程师-物理隔离技术基础核心考点解析
开发语言·网络·安全·软考·信息安全工程师·软件水平考试
空中海2 小时前
02 状态、Hooks、副作用与数据流
开发语言·javascript·ecmascript
Aurorar0rua2 小时前
CS50 x 2024 Notes C - 09
c语言·开发语言·学习方法