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