socket:tcp/udp、ip構成了網絡通訊的基石,tcp/ip是面向鏈接的通訊協議golang
要求創建鏈接時進行3次握手確保鏈接已被創建,關閉鏈接時須要4次通訊來保證客戶端和,服務端都已經關閉編程
在通訊過程當中還有保證數據不丟失,在鏈接不順暢通時還須要進行超時重試等等網絡
因此socket就是封裝了這一套基於tcp/udp/ip協議細節,提供了一系列套接字接口進行通訊socket
client端經過如下方式與Server端進行通訊tcp
先看看再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相關的底層函數spa
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封裝的。code
socket:建立的socket默認是阻塞的,經過syscall.SetNonblock()能夠將socket設置爲非阻塞模式blog
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) }