源码阅读 Go net包 待续

本文详细介绍了Go语言中的net包,它提供了一组接口用于网络I/O操作,包括TCP/IP、UDP、域名解析和Unix域套接字。net包的主要功能集中在Dial、Listen和Accept等函数上,适用于大部分客户端的网络通信需求。通过Listener接口,可以实现对网络连接的管理和并发处理。文中还展示了如何使用Listen方法创建TCP监听器,并通过示例代码说明了监听器的工作流程,包括接受连接、处理请求及关闭连接等步骤。此外,还深入探讨了底层的socket系统调用和文件描述符的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

net 包

Package net provides a portable interface for network I/O, including TCP/IP, UDP, domain name resolution, and Unix domain sockets.

net包为包括TCP/IP、UDP、域名解析和Unix 域套接字网络IO提供了简便的接口

Although the package provides access to low-level networking primitives, most clients will need only the basic interface provided by the Dial, Listen, and Accept functions and the associated Conn and Listener interfaces. The crypto/tls package uses the same interfaces and similar Dial and Listen functions.

尽管net包提供了访问底层网络的原语,但大多数客户端只需要 Dial、Listen 和 Accept 函数以及相关的 Conn 和 Listener 接口提供的基本接口。crypto/tls 包使用相同的接口和类似的 Dial 和 Listen 功能。

net包的Go语言是访问网络基础包,下面学习一下几个方法。

Listen方法

type Listener

type Listener interface {
	// Accept waits for and returns the next connection to the listener.
	// Accept 等待并将下一个连接返回给侦听器。
	Accept() (Conn, error)

	// Close closes the listener.Close 关闭监听器。
	// Any blocked Accept operations will be unblocked and return errors.  任何被阻塞的 Accept 操作都将被解除阻塞并返回错误。
	Close() error

	// Addr returns the listener's network address.
    // Addr 返回监听器的网络地址。
	Addr() Addr
}

A Listener is a generic network listener for stream-oriented protocols.

Listener是面向流协议的通用网络侦听器。

Multiple goroutines may invoke methods on a Listener simultaneously.

多个 goroutine 可以同时调用 Listener 上的方法。

一个简单的例子:

package main

import (
	"io"
	"log"
	"net"
)

func main() {
	// Listen on TCP port 2000 on all available unicast and
	// anycast IP addresses of the local system.
	l, err := net.Listen("tcp", ":2000")
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()
	for {
		// Wait for a connection.
		conn, err := l.Accept()
		if err != nil {
			log.Fatal(err)
		}
		// Handle the connection in a new goroutine.
		// The loop then returns to accepting, so that
		// multiple connections may be served concurrently.
		go func(c net.Conn) {
			// Echo all incoming data.
			io.Copy(c, c)
			// Shut down the connection.
			c.Close()
		}(conn)
	}
}

func Listen

ListeListen announces on the local network address.

The network must be “tcp”, “tcp4”, “tcp6”, “unix” or “unixpacket”.For TCP networks, if the host in the address parameter is empty or a literal unspecified IP address, Listen listens on all available unicast and anycast IP addresses of the local system. To only use IPv4, use network “tcp4”. The address can use a host name, but this is not recommended, because it will create a listener for at most one of the host’s IP addresses. If the port in the address parameter is empty or “0”, as in “127.0.0.1:” or “[::1]:0”, a port number is automatically chosen. The Addr method of Listener can be used to discover the chosen port.

func Listen(network, address string) (Listener, error) {
    // 创建一个默认的ListenConfig 配置对象
	var lc ListenConfig
	return lc.Listen(context.Background(), network, address)
}
func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (Listener, error) {
    // 解析地址 address
	addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil)
	if err != nil {
		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
	}
    // 创建一个sysListener对象,包含了Listen的参数和配置
	sl := &sysListener{
		ListenConfig: *lc,
		network:      network,
		address:      address,
	}
    // 创建一个Listen对象
	var l Listener
    // 返回第一个Ipv4地址
	la := addrs.first(isIPv4)
	switch la := la.(type) {
        // TCP地址
	case *TCPAddr:
		l, err = sl.listenTCP(ctx, la)
        // Unix地址
	case *UnixAddr:
		l, err = sl.listenUnix(ctx, la)
	default:
        // 其他类型直接返回错误
		return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
	}
	if err != nil {
		return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: err} // l is non-nil interface containing nil pointer
	}
    // 返回Listener 对象
	return l, nil
}
func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) {
	// 调用内部的Socket方法,获取fd
    fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", sl.ListenConfig.Control)
	if err != nil {
		return nil, err
	}
    // 创建TCPListener对象
	return &TCPListener{fd: fd, lc: sl.ListenConfig}, nil
}

FD

文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIXLinux这样的操作系统。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7XTx2Bk3-1663319427738)(https://upload.wikimedia.org/wikipedia/commons/f/f8/File_table_and_inode_table.svg)]

func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlFn func(string, string, syscall.RawConn) error) (fd *netFD, err error) {
   if (runtime.GOOS == "aix" || runtime.GOOS == "windows" || runtime.GOOS == "openbsd") && mode == "dial" && raddr.isWildcard() {
      raddr = raddr.toLocal(net)
   }
   // 获取合适的地址族
   family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
   return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, ctrlFn)
}
func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) (fd *netFD, err error) {
   s, err := sysSocket(family, sotype, proto)
   if err != nil {
      return nil, err
   }
   if err = setDefaultSockopts(s, family, sotype, ipv6only); err != nil {
      poll.CloseFunc(s)
      return nil, err
   }
   if fd, err = newFD(s, family, sotype, net); err != nil {
      poll.CloseFunc(s)
      return nil, err
   }

   // This function makes a network file descriptor for the
   // following applications:
   //
   // - An endpoint holder that opens a passive stream
   //   connection, known as a stream listener
   //
   // - An endpoint holder that opens a destination-unspecific
   //   datagram connection, known as a datagram listener
   //
   // - An endpoint holder that opens an active stream or a
   //   destination-specific datagram connection, known as a
   //   dialer
   //
   // - An endpoint holder that opens the other connection, such
   //   as talking to the protocol stack inside the kernel
   //
   // For stream and datagram listeners, they will only require
   // named sockets, so we can assume that it's just a request
   // from stream or datagram listeners when laddr is not nil but
   // raddr is nil. Otherwise we assume it's just for dialers or
   // the other connection holders.

   if laddr != nil && raddr == nil {
      switch sotype {
      case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET:
         if err := fd.listenStream(laddr, listenerBacklog(), ctrlFn); err != nil {
            fd.Close()
            return nil, err
         }
         return fd, nil
      case syscall.SOCK_DGRAM:
         if err := fd.listenDatagram(laddr, ctrlFn); err != nil {
            fd.Close()
            return nil, err
         }
         return fd, nil
      }
   }
   if err := fd.dial(ctx, laddr, raddr, ctrlFn); err != nil {
      fd.Close()
      return nil, err
   }
   return fd, nil
}

深入 sysSocket

// Wrapper around the socket system call that marks the returned file
// descriptor as nonblocking and close-on-exec.
func sysSocket(family, sotype, proto int) (int, error) {
    // 发起Socket 系统调用
	s, err := socketFunc(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto)
	// On Linux the SOCK_NONBLOCK and SOCK_CLOEXEC flags were
	// introduced in 2.6.27 kernel and on FreeBSD both flags were
	// introduced in 10 kernel. If we get an EINVAL error on Linux
	// or EPROTONOSUPPORT error on FreeBSD, fall back to using
	// socket without them.
	// 在 Linux 上,SOCK_NONBLOCK 和 SOCK_CLOEXEC 标志是在 2.6.27 内核中引入的,而在 FreeBSD 上,这两个标志都是在 10 内核中引入的。 如果我们在 Linux 上收到 EINVAL 错误或在 FreeBSD 上收到 EPROTONOSUPPORT 错误,请回退到在没有它们的情况下使用套接字。
	switch err {
	case nil:
		return s, nil
	default:
		return -1, os.NewSyscallError("socket", err)
	case syscall.EPROTONOSUPPORT, syscall.EINVAL:
	}

	// See ../syscall/exec_unix.go for description of ForkLock.
	syscall.ForkLock.RLock()
	s, err = socketFunc(family, sotype, proto)
	if err == nil {
		syscall.CloseOnExec(s)
	}
	syscall.ForkLock.RUnlock()
	if err != nil {
		return -1, os.NewSyscallError("socket", err)
	}
	if err = syscall.SetNonblock(s, true); err != nil {
		poll.CloseFunc(s)
		return -1, os.NewSyscallError("setnonblock", err)
	}
	return s, nil
}

syscall.Socket 系统调用

var (
   testHookDialChannel  = func() {} // for golang.org/issue/5349
   testHookCanceledDial = func() {} // for golang.org/issue/16523

   // Placeholders for socket system calls.
   socketFunc        func(int, int, int) (int, error)  = syscall.Socket // Socket 调用函数
   connectFunc       func(int, syscall.Sockaddr) error = syscall.Connect 
   listenFunc        func(int, int) error              = syscall.Listen
   getsockoptIntFunc func(int, int, int) (int, error)  = syscall.GetsockoptInt
)
func Socket(domain, typ, proto int) (fd int, err error) {
   if domain == AF_INET6 && SocketDisableIPv6 {
      return -1, EAFNOSUPPORT
   }
   fd, err = socket(domain, typ, proto)
   return
}
func socket(domain int, typ int, proto int) (fd int, err error) {
   r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
   fd = int(r0)
   if e1 != 0 {
      err = errnoErr(e1)
   }
   return
}
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
智能网联汽车的安全员高级考试涉及多个方面的专业知识,包括但不限于自动驾驶技术原理、车辆传感器融合、网络安全防护以及法律法规等内容。以下是针对该主题的一些核心知识解析: ### 关于智能网联车安全员高级考试的核心内容 #### 1. 自动驾驶分级标准 国际自动机工程师学会(SAE International)定义了六个级别的自动驾驶等级,从L0到L5[^1]。其中,L3及以上级别需要安全员具备更高的应急处理能力。 #### 2. 车辆感知系统的组成与功能 智能网联车通常配备多种传感器,如激光雷达、毫米波雷达、摄像头和超声波传感器等。这些设备协同工作以实现环境感知、障碍物检测等功能[^2]。 #### 3. 数据通信与网络安全 智能网联车依赖V2X(Vehicle-to-Everything)技术进行数据交换,在此过程中需防范潜在的网络攻击风险,例如中间人攻击或恶意软件入侵[^3]。 #### 4. 法律法规要求 不同国家和地区对于无人驾驶测试及运营有着严格的规定,考生应熟悉当地交通法典中有关自动化驾驶部分的具体条款[^4]。 ```python # 示例代码:模拟简单决策逻辑 def decide_action(sensor_data): if sensor_data['obstacle'] and not sensor_data['emergency']: return 'slow_down' elif sensor_data['pedestrian_crossing']: return 'stop_and_yield' else: return 'continue_driving' example_input = {'obstacle': True, 'emergency': False, 'pedestrian_crossing': False} action = decide_action(example_input) print(f"Action to take: {action}") ``` 需要注意的是,“同学”作为特定平台上的学习资源名称,并不提供官方认证的标准答案集;建议通过正规渠道获取教材并参加培训课程来准备此类资格认证考试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值