白话Golang网络编程基础

目录

白话网络编程基础

白话

Socket概念

如何理解Socket(套接字)

Socket 基本通信流程

浅析TCP连接建立过程

总结

文章引用


白话网络编程基础

白话

大家好,我是编程小灶。首先谈谈落笔这一系列关于Golang网络编程文章的原因,在工作中小灶涉及存储网关开发,在解决一些性能与难点后,小灶觉得应该对网络编程原理做更深入理解,因此基于Golang实现了网络库(Knetty),同时借用此专栏将Golang网络编程知识做一些沉淀,希望有幸帮助有兴趣/从事Golang网络编程相关的朋友。笔者能力有限,有疑问/理解有误的地方及时指正。进入正题,今天我们来讨论一下老生常谈的"网络编程"。

  1. 本系列文章主要围绕TCP协议。

  2. 本系列文章主要围绕Golang编程语言。

Socket概念

谈到网络编程,"Socket"对于大家并不陌生,它也是我们系列文章核心对象,其百度百科的定义为

 所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口.            

从百科的描述,不难看出Socket处在传输层和应用层之间,是应用程序与网络协议栈进行交互的接口。它在OSI七层模型/网际协议层 的位置如下图所示

白话Golang网络编程基础_第1张图片

从上图右侧网络协议族来看,Socket是应用层进入传输层的接口,同时OSI模型下四层通常由操作系统内核提供,所以Socket在网络通信中也提供分割用户进程与操作系统内核的机制。本系列文章重点也是讲述通过Golang Socket构建支持TCP协议的网络通信框架(后文会详细介绍)。

如何理解Socket(套接字)

Socket到底是什么,如下方式可以在Golang中创建一个IPV4协议,面向TCP协议的Socket(套接字)

// 第一个参数(协议域) unix.AF_INET 代表 IPV4协议
// 第二个参数 (套接字类型) unix.SOCK_STREAM 代表 字节流类型
// 第三个参数(协议) unix.IPPROTO_TCP 代表 TCP协议(默认情况下可以传0)
SocketFD, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM,syscall.IPPROTO_TCP)
	if err != nil {
		return nil, err
}

调用syscall.Socket函数操作系统返回了一个与文件描述符类似的非负数fd,我们称之为套接字文件描述符( Socket descriptor ).文章中简称SocketFD

Golang系统调用提供了诸多网络编程函数如

// 挑选了一些关键函数 
func Socket(domain, typ, proto int) (fd int, err error)
func Bind(fd int, sa Sockaddr) (err error)
func Accept(fd int) (nfd int, sa Sockaddr, err error)
func Connect(fd int, sa Sockaddr) (err error)
func Read(fd int, p []byte) (n int, err error)
func Write(fd int, p []byte) (n int, err error)
func Close(fd int) (err error)
.....

如上函数都有一个共同点,即所有行为都是在 SocketFD 基础上进行。依靠如上函数可以完成数据在应用进程和操作系统内核之间的传递,也更直观表达了 Socket是应用程序与网络协议栈进行交互的接口。

简单来说,Socket在应用进程视角就是一个数字 FD(非负数),客户端和服务端应用进程分别持有SocketFD来完成两个远程进程间的通信。

Socket 基本通信流程

在正式进入通信流程部分,我们简单回顾一下在网络传输层中,客户端如果寻找到目标服务器并与其建立连接。我们接着上文如何理解Socket(套接字)中一个IPV4协议域,面向TCP协议的Socket(套接字)来看,创建好SocketFD后,需要调用Connect函数来连接目标服务器。

// 参数一 即客户端通过Socket函数创建的SocketFD
// 参数二 即网络层需要的套接字地址结构(目标服务器地址)
func Connect(fd int, sa Sockaddr) (err error)

一个连接本地端口8888的案例,连接时,客户端会选用随机端口与服务端建立连接。

// 设置服务器地址
	serverAddr := &syscall.SockaddrInet4{
		Port: 8888,
		Addr: [4]byte{127, 0, 0, 1},
	}

	// 连接到服务器
	syscall.Connect(Socket, serverAddr)

Sockaddr接口定义在源码syscall_unix.go中,我们挑选了IPV4套接字地址结构在Golang中的定义

// IPV4 协议定义
type SockaddrInet4 struct {
	Port int  // 服务器端口
	Addr [4]byte // 服务器地址 ipv4 结构
	raw  RawSockaddrInet4 
}

// 调用内核IPV4协议定义
type RawSockaddrInet4 struct {
	Len    uint8 // SizeofSockaddrInet4
	Family uint8 // AF_INET
	Port   uint16 // 服务器端口
	Addr   [4]byte /* in_addr */ 服务器地址
	Zero   [8]int8 // 默认为空,补充字段
}

对于服务端而言,在调用bind函数时,需要指定套接字地址结构

// 参数一 即服务端通过Socket函数创建的SocketFD
// 参数二 即网络层需要的套接字地址结构(本主机地址)
func Bind(fd int, sa Sockaddr) (err error)

一个绑定本地8888端口的案例

syscall.Bind(fd, &syscall.SockaddrInet4{Port: 8888})

如上客户端和服务端的套接字地址组成了四元祖来辨识一个唯一TCP的网络连接,从客户端角度来看整个连接如下

白话Golang网络编程基础_第2张图片

回顾了网络连接如何建立,如下是一个CS模型下TCP Socket通信的基本过程,图中从上至下,以此展示了服务端和客户端通信的行为。Golang-Socket-example在unix系统下实现了上图交互过程

白话Golang网络编程基础_第3张图片

 上图展示了客户端、服务端通过Socket使用TCP协议完成的一个常见且标准的交互过程。我们本系列网络编程核心也都是围绕如上交互过程做优化。

浅析TCP连接建立过程

  1. 图二中服务端执行listen函数,服务端Socket状态从CLOSED转为LISTEN状态。

  2. 客户端执行connect函数发起连接请求,开始三次握手,当客户端SYN到达服务端,TCP未完成连接队列新增一项,服务端响应客户端SYN.

  3. 三次握手结束第三步(客户端回应服务端SYN的ACK)后,此客户端发起的连接项从未完成队列进入已完成队列尾部。

  4. 服务端accept函数返回已就绪套接字描述符(SocketFD);客户端connect函数正常返回。

总结

在这篇文章中主要介绍了Socket的概念,讲解了套接字是如何上联应用进程,下联网络协议栈。以Golang 中Socket编程为案例讲解了TCP,IPV4协议如何建立连接和基本的通信过程。

感谢你的阅读,欢迎留言讨论,如何对你理解有帮助也欢迎推荐给更多的朋友。

文章引用

公众号:编程小灶

白话Golang网络编程基础_第4张图片

你可能感兴趣的:(Go,golang,开发语言,后端,网络编程)