• golang socket 实现分析(一)


    socket:tcp/udp、ip构成了网络通信的基石,tcp/ip是面向连接的通信协议

                要求建立连接时进行3次握手确保连接已被建立,关闭连接时需要4次通信来保证客户端和,服务端都已经关闭

                在通信过程中还有保证数据不丢失,在连接不畅通时还需要进行超时重试等等

                所以socket就是封装了这一套基于tcp/udp/ip协议细节,提供了一系列套接字接口进行通信

      

    client端通过以下方式与Server端进行通信

    先看看再golang中如何进行socket编程

    // 创建socket文件描述符,绑定ip:port,改变socket状态为监听状态
    ln, err := net.Listen("tcp", addr)
    // 返回时关闭tcp连接
    defer l.Close()
    if err != nil {
        return err
    }
    for {
        // 从socket recive队列里获取一个建立好的连接
        conn,err := ln.Accept()
        if err != nil {
            return err
        }
        // 新起一个goroutine处理连接
        go handler(conn)
    }
    
    func handler(conn net.Con) {
        // 关闭连接
        conn.Close()
    }

    介绍几个跟socket相关的底层函数

    socketFunc func(int, int, int) (int, error) = syscall.Socket //创建一个socket文件描述符
    func Bind(fd int, sa Sockaddr) (err error) //绑定一个本机IP:port到socket文件描述符上
    listenFunc func(int, int) error = syscall.Listen //监听是否有tcp连接请求
    acceptFunc func(int) (int, syscall.Sockaddr, error) = syscall.Accept //获取一个建立好的tcp连接
    connectFunc func(int, syscall.Sockaddr) error = syscall.Connect //发起tcp连接请求
    closeFunc func(int) error = syscall.Close //关闭连接

    下面介绍下在golang中socket接口是如何通过这几个底层函数完成socket封装的。

     socket:创建的socket默认是阻塞的,通过syscall.SetNonblock()可以将socket设置为非阻塞模式

    func sysSocket(family, sotype, proto int) (int, error) {
    syscall.ForkLock.RLock()
    //创建socket文件描述符 s, err :
    = socketFunc(family, sotype, proto) if err == nil {
         // 关闭从父线程拷贝过来的文件描述符后,再执行子线程程序 syscall.CloseOnExec(s) } syscall.ForkLock.RUnlock()
    if err != nil { return -1, os.NewSyscallError("socket", err) }

    //设置socket位非阻塞
    if err = syscall.SetNonblock(s, true); err != nil { closeFunc(s) return -1, os.NewSyscallError("setnonblock", err) } return s, nil } 

    listen:设置socket文件描述符为监听状态,把监听到的请求放入未完成的请求队列中,完成3次握手后,会把连接放入已完成的请求队列中等待accept获取处理

    func (fd *netFD) listenStream(laddr sockaddr, backlog int) error {
        if err := setDefaultListenerSockopts(fd.sysfd); err != nil {
            return err
        }
        if lsa, err := laddr.sockaddr(fd.family); err != nil {
            return err
        } else if lsa != nil {
           //绑定ip:port
            if err := syscall.Bind(fd.sysfd, lsa); err != nil {
                return os.NewSyscallError("bind", err)
            }
        }
        //监听socket文件描述符
        if err := listenFunc(fd.sysfd, backlog); err != nil {
            return os.NewSyscallError("listen", err)
        }
        if err := fd.init(); err != nil {
            return err
        }
        lsa, _ := syscall.Getsockname(fd.sysfd)
        fd.setAddr(fd.addrFunc()(lsa), nil)
        return nil
    }

    accept:从已完成的队列里取出一个tcp连接,返回的是由内核根据当前socket信息创建的全新的tcp连接来处理数据的,同时原始创建好的socket还可以继续监听其他连接请求,如果没有获取到则阻塞当前goroutine

    func accept(s int) (int, syscall.Sockaddr, error) {
    //获取连接 ns, sa, err := acceptFunc(s) if err == nil { syscall.CloseOnExec(ns) } if err != nil { return -1, nil, os.NewSyscallError("accept", err) }
    //设置为非阻塞 if err = syscall.SetNonblock(ns, true); err != nil { closeFunc(ns) return -1, nil, os.NewSyscallError("setnonblock", err) } return ns, sa, nil }

    connect:client端发起连接请求

    //发起连接请求
    func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time, cancel <-chan struct{}) error {
    switch err := connectFunc(fd.sysfd, ra); err {
      //异常处理
        ....
    }
    }

    close:

    //关闭连接请求
    func (fd *netFD) destroy() {
    //关闭连接 fd.pd.Close()
    //释放系统资源 closeFunc(fd.sysfd) fd.sysfd
    = -1 runtime.SetFinalizer(fd, nil) }
  • 相关阅读:
    批处理+7zip解压用纯数字加密的压缩包zip
    golang 读取 chrome保存的网站账号信息
    c++实现"扫描检测硬件改动"
    c++获取磁盘句柄
    golang设置title并获取窗口句柄
    golang获取文件的md5
    golang获取u盘序列号(通过读取注册表实现)
    golang从文件按行读取并输出
    golang cgo注意事项
    python调用远程chromedriver.exe、selenium抓包方法
  • 原文地址:https://www.cnblogs.com/zongjiang/p/6569342.html
Copyright © 2020-2023  润新知