go 语言socket 编程

简介

Socket,英文含义是【插座、插孔】,一般称之为套接字,用于描述IP地址和端口。可以实现不同程序间的数据通信。

Socket起源于Unix,而Unix基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用:Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

  • 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
  • 建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
  • Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。插座是用来给插头提供一个接口让其通电的,此时我们就可以将插座当做一个服务端,不同的插头当做客户端。
服务端
package main

import (
    "fmt"
    "net"
    "strings"
)

func main() {
    listener ,err := net.Listen("tcp",":8000")
    if err != nil {
        fmt.Println("listen err :",err)
    }

    defer listener.Close()
    fmt.Println("等待客户端连接...")

    // 接收多个用户
    for {
        conn ,err := listener.Accept()
        if err != nil {
            fmt.Println("err=",err)
            return
        }
        go HandleConn(conn)
    }

/*  conn, err := listener.Accept()
    if err != nil {
        fmt.Println("accept err:",err)
    }
    defer conn.Close()
    fmt.Println("客户端与服务器连接建立成功...")

    buf := make([]byte,1024)

    n,err := conn.Read(buf)

    if err != nil {
        fmt.Println("read err: ",err)
    }
    _,err = conn.Write([]byte("收到了"))
    if err != nil {
        fmt.Println("收到了:",err)
    }
    fmt.Println("服务器读到数据:",string(buf[:n]))*/
}

func HandleConn(conn net.Conn) {
    //函数调用完毕,自动关闭conn
    defer conn.Close()

    //获取客户端的网络地址信息
    addr := conn.RemoteAddr().String()
    fmt.Println(addr, " conncet sucessful")

    buf := make([]byte, 2048)


    //把数据转换为大写,再给用户发送 这个地方for 是为了客户端和服务端保持连接不中断
    for {
        //读取用户数据
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("err = ", err)
            return
        }
        fmt.Printf("[%s]: %s\n",  addr,  string(buf[:n]))
        fmt.Println("len = ", len(string(buf[:n])))

        //if "exit" == string(buf[:n-1]) {  // nc测试,发送时,只有 \n
        if  "exit" == string(buf[:n-1]) {  // 自己写的客户端测试, 发送时,多了2个字符, "\r\n"
            fmt.Println(addr, " exit")
            return
        }
        fmt.Println("ok",string(buf[:n]))
        conn.Write([]byte(strings.ToUpper(string(buf[:n]))))
    }
}

客户端

package main

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

func main() {
    conn,err := net.Dial("tcp","127.0.0.1:8000")

    if err != nil {
        fmt.Println("Dial err:",err)
    }

    defer conn.Close()


    // 启动子协程,接收用户键盘输入
    go func() {
        str := make([]byte, 1024)         // 创建用于存储用户键盘输入数据的切片缓冲区。
        for {                             // 反复读取
            n, err :=os.Stdin.Read(str)        // 获取用户键盘输入
            if err != nil {
                fmt.Println("os.Stdin.Read err:", err)
                return
            }
            // 将从键盘读到的数据,发送给服务器
            _, err = conn.Write(str[:n])       // 读多少,写多少
            if err != nil {
                fmt.Println("conn.Write err:", err)
                return
            }
        }
    }()

    // 主协程,接收服务器回发数据,打印至屏幕
    buf := make([]byte, 1024)                // 定义用于存储服务器回发数据的切片缓冲区
    for {
        n, err := conn.Read(buf)          // 从通信 socket 中读数据,存入切片缓冲区
        if err != nil {
            fmt.Println("conn.Read err:", err)
            return
        }
        fmt.Printf("服务器回发:%s\n", string(buf[:n]))
    }

/*  n,err := conn.Write([]byte("are you ok"))
    if err != nil {
        fmt.Println("write err:",err)
    }
    fmt.Println(n)

    buf := make([]byte,1024)

    n,err = conn.Read(buf)

    if err != nil {
        fmt.Println("read err: ",err)
    }
    fmt.Println("客户端读到数据:",string(buf[:n]))*/
}

参考文献

https://www.jianshu.com/p/dc8e23de0367

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容