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) }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM