一、客户端/服务器架构

1.硬件C/S架构(打印机)

打印机作为一个服务端,电脑连接打印机进行打印

2.软件C/S架构

  互联网中处处是C/S架构

  如谷歌网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种)

  腾讯作为服务端为你提供视频,你得下个腾讯视频客户端才能看它的视频)

C/S架构与socket的关系:

我们学习socket就是为了完成C/S架构的开发

二、什么是tcp/ip协议

​ 计算机之间通讯需要遵循一定的互联网协议,比如tcp/ip协议,大量的计算机之间进行通讯组成了计算机网络,网络的核心即一堆协议,协议即标准,由于tcp/ip协议太过于复杂,这时需要用socket层对tcp/ip协议进行精简,提供相应的接口以便更加方便的调用。所以学习socket一定要先学习互联网协议。

互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

每层运行常见物理设备

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

TCP/IP 是一类协议系统,它是用于网络通信的一套协议集合,传统上来说 TCP/IP 被认为是一个四层协议。

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

1) 网络接口层:

物理层:主要是指物理层次的一些接口,比如电缆等.

提供计算机互通的物理介质, 基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0

数据链路层:

定义电信号的分组方式,使用以太网协议封装数据格式(数据报头(18个字节双方地址+数据类型)+数据)

2) 网络层:

提供独立于硬件的逻辑寻址,实现物理地址与逻辑地址的转换.

在 TCP / IP 协议族中,网络层协议包括 IP 协议(网际协议),ICMP 协议( Internet 互联网控制报文协议),以及 IGMP 协议( Internet 组管理协议).

网络层引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址(IPV4/IPV6)

3) 传输层:

为网络提供了流量控制,错误控制和确认服务.

在 TCP / IP 协议族中有两个互不相同的传输协议: TCP(传输控制协议)和 UDP(用户数据报协议).

建立端口到端口的通信(应用软件使用的端口号)。

4) 应用层:

为网络排错,文件传输,远程控制和 Internet 操作提供具体的应用程序

数据包在 TCP / IP 协议中数据先由上往下将数据装包,然后由下往上拆包

在装包的时候,每一层都会增加一些信息用于传输,这部分信息就叫报头,当上层的数据到达本层的时候,会将数据加上本层的报头打包在一起,继续往下传递.

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

在拆包的时候,每一层将本层需要的报头读取后,就将剩下的数据往上传.

这个过程有点像俄罗斯套娃,所以有时候人们也会用俄罗斯套娃来形容这个过程.

tcp的三次握手四次挥手

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

三次握手:1.客户端对服务端发送同步请求(建立连接通道);2.服务端响应后,发送同步请求(建立连接通道)和发送确认数据(这里的两个请求被合并成一个了);3.客户端向服务端发送确认数据。

四次挥手:1.客户端发送端口结束包给服务端,2.服务端收到以后返回确认信息给客户端;3.服务端发送端口结束包给客户端;4.客户端确认信息返回给服务端。

区别:四次挥手的过程中,服务端返回给客户端的确认信息之后不会立刻关闭,有可能会继续给客户端发送数据,所以必须得有四次挥手

三、socket是什么

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

没有socket之前

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

有socket之后

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

为什么socket翻译为套接字?

socket中文翻译为套接字,socket英文本义为孔和插座的意思。

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

套接字发展史及分类

套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。

*基于文件类型的套接字家族*

套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

*基于网络类型的套接字家族*

套接字家族的名字:AF_INET

还有AF_INET6被用于ipv6,还有一些其他的地址家族,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族.

*family(socket家族)*

  • socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成

  • socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

*socket type类型*

  • socket.SOCK_STREAM #for tcp

  • socket.SOCK_DGRAM #for udp

  • socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

  • socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。

  • socket.SOCK_SEQPACKET #废弃了

套接字工作流程

一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理。

​ 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束.

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

Python之路(第三十篇) 网络编程:socket、tcp/ip协议-LMLPHP

socket函数

服务端套接字函数

  • s.bind() 绑定(主机,端口号)到套接字

  • s.listen() 开始TCP监听

  • s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数

  • s.connect() 主动初始化TCP服务器连接

  • s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数

  • s.recv() 接收数据

  • s.send() 发送数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完,可后面通过实例解释)

  • s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)

  • s.sendto() 发送UDP数据

  • s.recvfrom()接收UDP数据 Receive data from the socket. The return value is a pair (bytes, address)

  • s.getpeername() 连接到当前套接字的远端的地址

  • s.close() 关闭套接字

  • socket.setblocking(flag) #True or False,设置socket为非阻塞模式,以后讲io异步时会用

  • socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) 返回远程主机的地址信息,例子 socket.getaddrinfo('google.com',80)

  • socket.getfqdn() 拿到本机的主机名

  • socket.gethostbyname() 通过域名解析ip地址

  • s.getsockopt() 返回指定套接字的参数

  • s.setsockopt() 设置指定套接字的参数

面向锁的套接字方法

  • s.setblocking() 设置套接字的阻塞与非阻塞模式

  • s.settimeout() 设置阻塞套接字操作的超时时间

  • s.gettimeout() 得到阻塞套接字操作的超时时间

面向文件的套接字的函数

  • s.fileno() 套接字的文件描述符

  • s.makefile() 创建一个与该套接字相关的文件

参考链接

[1]https://blog.csdn.net/yulyu/article/details/69062288

[2]https://www.zhihu.com/question/21383903

04-28 19:27