46 | 访问网络服务
前导内容:socket 与 IPC
人们常常会使用 Go 语言去编写网络程序(当然了,这方面也是 Go 语言最为擅长的事情)。说到网络编程,我们就不得不提及 socket。
socket,常被翻译为套接字,它应该算是网络编程世界中最为核心的知识之一了。关于 socket,我们可以讨论的东西太多了,因此,我在这里只围绕着 Go 语言向你介绍一些关于它的基础知识。
所谓 socket,是一种 IPC 方法。IPC 是 Inter-Process Communication 的缩写,可以被翻译为进程间通信。顾名思义,IPC 这个概念(或者说规范)主要定义的是多个进程之间,相互通信的方法。
这些方法主要包括:系统信号(signal)、管道(pipe)、套接字 (socket)、文件锁(file lock)、消息队列(message queue)、信号灯(semaphore,有的地方也称之为信号量)等。现存的主流操作系统大都对 IPC 提供了强有力的支持,尤其是 socket。
你可能已经知道,Go 语言对 IPC 也提供了一定的支持。
比如,在os代码包和os/signal代码包中就有针对系统信号的 API。
又比如,os.Pipe函数可以创建命名管道,而os/exec代码包则对另一类管道(匿名管道)提供了支持。对于 socket,Go 语言与之相应的程序实体都在其标准库的net代码包中。
毫不夸张地说,在众多的 IPC 方法中,socket 是最为通用和灵活的一种。与其他的 IPC 方法不同,利用 socket 进行通信的进程,可以不局限在同一台计算机当中。
实际上,通信的双方无论存在于世界上的哪个角落,只要能够通过计算机的网卡端口以及网络进行互联,就可以使用 socket。
支持 socket 的操作系统一般都会对外提供一套 API。跑在它们之上的应用程序利用这套 API,就可以与互联网上的另一台计算机中的程序、同一台计算机中的其他程序,甚至同一个程序中的其他线程进行通信。
例如,在 Linux 操作系统中,用于创建 socket 实例的 API,就是由一个名为socket的系统调用代表的。这个系统调用是 Linux 内核的一部分。
所谓的系统调用,你可以理解为特殊的 C 语言函数。它们是连接应用程序和操作系统内核的桥梁,也是应用程序使用操作系统功能的唯一渠道。
在 Go 语言标准库的syscall代码包中,有一个与这个socket系统调用相对应的函数。这两者的函数签名是基本一致的,它们都会接受三个int类型的参数,并会返回一个可以代表文件描述符的结果。
但不同的是,syscall包中的Socket函数本身是平台不相关的。在其底层,Go 语言为它支持的每个操作系统都做了适配,这才使得这个函数无论在哪个平台上,总是有效的。
package main
import (
"fmt"
"syscall"
)
func main() {
fd1, err := syscall.Socket(
syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err != nil {
fmt.Printf("socket error: %v\n", err)
return
}
defer syscall.Close(fd1)
fmt.Printf("The file descriptor of socket:%d\n", fd1)
// 省略若干代码。
// 如果真要完全使用syscall包中的程序实体建立网络连接的话,
// 过程太过繁琐而且完全没有必要。
// 所以,我在这里就不做展示了。
}
Go 语言的net代码包中的很多程序实体,都会直接或间接地使用到syscall.Socket函数。
比如ÿ