python和网络编程

网络编程

一、引子

假如有两个脚本,a.pyb.py,都可以正常运行。假如现在想从两个程序之间传递一个数据。

  • 同一台电脑
    • 创建一个文件,将a.py的数据读入文件中,b.py从文件中读取数据。
  • 不同电脑间

二、软件的开发架构

C/S架构

C/S 即 Client 和 Server(客户端和服务器端架构)

B/S架构

B/S 即 Browser 和 Server(浏览器和服务器端架构)

三、网络

辅助双方或多方能够连接在一起的工具。

伴随着网络发展,人们使用了很多通信方法,有些已不再使用,现在使用最广泛的是TCP/IP(Transmission Control Protocol,Internet Protocol)。TCPMP是标准的协议,其可以使用时间范围内的计算机通过Internet或本地网络通信。

TCP/IP事实上是一些协议(protocols)的合集。当前大多数使用中的通信都使用TCP协议。

Internet是在一些共享的线路上发送数据’的。例如:在您的计算机上也许同时运行着几个应用程序,如Web浏览器、通讯软件等程序,而您只须通过一条单一的线路来连接互联网。上面所有的程序都共享这个连接,简单地说,用户往往不会觉察到这个共享的发生。

四、目的

  • 使用网络把多方连接在一起,然后,进行数据传输
  • 为了让不同电脑的软件可以互相传递数据,借助网络的功能

五、网络编程的定义

让不同电脑软件能够进行数据传递,即网络中不同主机进程间的通信

六、地址

1. 生活中的地址与标识

不同的网络中,采用唯一的标识来区分不同的主体,比如车牌号、建筑物地址、电话号码、员工编号等等

一台电脑怎么找到很多电脑中的一台呢?

警察怎么找到嫌疑犯的?

2. ip地址的作用

ip地址:用来在网络中标记一台电脑,比如192.168.1.1;在同一网络中是唯一的。

同一网络:好比班级编号,球队编号。

同一网络:例如同一个局域网, 一个教室里。

3.ip地址分类

ip主要有两种:

  • ipv4,32位二进制构成
  • ipv6,128位二进制构成

每个ip包含两部分:

  • 网络机
  • 主机号
    • 类似电话号码由区号+电话主机号组成
以太网适配器 以太网:

   连接特定的 DNS 后缀 . . . . . . . :
   IPv6 地址 . . . . . . . . . . . . : 2001:250:6405:6005:50cb:b469:3eb4:e270
   临时 IPv6 地址. . . . . . . . . . : 2001:250:6405:6005:7544:e38f:8699:f850
   本地链接 IPv6 地址. . . . . . . . : fe80::50cb:b469:3eb4:e270%18
   IPv4 地址 . . . . . . . . . . . . : 192.168.1.130
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . : fe80::22a:6aff:fefa:ff3c%18
                                       192.168.1.1

(1) IPv4可提供4,294,967,296个地址,IPv6将原来的32位地址空间增大到128位,数目是2的128次方。能够对地球上每平方米提供6×1023个网络地址,在可预见的将来是不会耗尽的。   
(2) IPv4 使用地址解析通讯协议 (ARP) ,IPv6使用用多点传播 Neighbor Solicitation 消息取代地址解析通讯协议 (ARP) 。   
(3) IPv4 中路由器不能识别用于服务质量的QoS 处理的 payload。IPv6中路由器使用 Flow Label 字段可以识别用于服务质量的 QoS 处理的 payload。      
(4) IPv4的回路地址为: 127.0.0.1,IPv6的回路地址为 : 000:0000:0000:0000:0000:0000:0000:0001 可以简写为 ::1。   
(5) 在IPv4中,动态主机配置协议( Dynamic Host ConfigurationProtocol,DHCP)实现了主机IP地址及其相关配置的自动设置。一个DHCP服务器拥有一个IP地址池,主机从DHCP服务器租借IP地址并获得有关的配置信息(如缺省网关、DNS服务器等),由此达到自动设置主机IP地址的目的。IP v6继承了IPv4的这种自动配置服务,并将其称为全状态自动配置(stateful autoconfiguration)。 m.pcwenku.com 供稿   
(6) IPv4使用 Internet 群组管理通讯协议 (IGMP) 管理本机子网络群组成员身份,IPv6使用 Multicast Listener Discovery (MLD) 消息取代 IGMP。   
(7) 内置的安全性。IPSec由IETF开发是确保秘密、完整、真实的信息穿越公共IP网的一种工业标准。IPsec不再是IP协议的补充部分,在IPv6中IPsec是IPv6自身所具有的功能。IPv4选择性支持IPSec,IPv6自动支持IPSec。 (8) 更好的QoS支持。QoS是网络的一种安全机制,通常情况下不需要QoS,但是对关键应用和多媒体应用就十分必要。当网络过载或拥塞时,QoS 能确保重要业务量不受延迟或丢弃,同时保证网络的高效运行。在IPv6 的包头中定义了如何处理与识别传输, IPv6 包头中使用 Flow Label 来识别传输,可使路由器标识和特殊处理属于一个流量的封包。流量是指来源和目的之间的一系列封包,因为是在 IPv6 包头中识别传输,所以即使透过 IPSec 加密的封包 payload,仍可实现对 QoS 的支持。

3.1 A类IP地址

一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”,

地址范围1.0.0.1-126.255.255.254

二进制表示为:00000001 00000000 00000000 00000001 - 01111110 11111111 11111111 11111110

8位二进制全1就是255

可用的A类网络有126个,每个网络能容纳1677214个主机

3.2 B类IP地址

一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,

地址范围128.1.0.1-191.255.255.254

二进制表示为:10000000 00000001 00000000 00000001 - 10111111 11111111 11111111 11111110

可用的B类网络有16384个,每个网络能容纳65534主机

3.3 C类IP地址

一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”

范围192.0.1.1-223.255.255.254

二进制表示为: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110

C类网络可达2097152个,每个网络能容纳254个主机

3.4 D类地址用于多点广播

D类IP地址第一个字节以“1110”开始,它是一个专门保留的地址。

它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中

多点广播地址用来一次寻址一组计算机

地址范围224.0.0.1-239.255.255.254

网上视频会议、网上视频点播就是采用多点广播

广播地址 (Broadcast Address) 是专门用于同时向同一网络中所有主机发送数据的一个地址。在使用TCP/IP 协议的网络中, 主机标识段 HOST ID 为全1 的IP 地址为广播地址,广播分组传送给 HOST ID 段所涉及的所有计算机. 例如, 对于 192.168.50.26(255.255.255.0) 网段,其广播地址为192.168.50.255, 当发出一个目的地址为 192.168.50.255 的数据时, 它将被分发给该网段上的所有计算机. 飞秋,内网通就是通过广播地址来广播数据的

3.5 E类IP地址

以“1111”开始,为将来使用保留

E类地址保留,仅作实验和开发用

3.6 私有ip

在这么多网络IP中,国际规定有一部分IP地址是用于我们的局域网使用,也就

是属于私网IP,不在公网中使用的,它们的范围是:

10.0.0.0~10.255.255.255

172.16.0.0~172.31.255.255

192.168.0.0~192.168.255.255
3.7 回路地址

IP地址127.0.0.1~127.255.255.255用于回路测试,

如:127.0.0.1可以代表本机IP地址,用http://127.0.0.1就可以测试本机中配置的Web服务器

七、网络通信过程

1. 2台电脑的网络

说明
  1. 如果两台电脑之间通过网线连接是可以直接通信的,但是需要提前设置好ip地址以及网络掩码
  2. 并且ip地址需要控制在同一网段内,例如 一台为192.168.1.1另一台为192.168.1.2则可以进行通信

2. 使用集线器组成一个网络

说明
  1. 当有多台电脑需要组成一个网时,那么可以通过集线器(Hub)将其链接在一起
  2. 一般情况下集线器的接口较少
  3. 集线器有个缺点,它以广播的方式进行发送任何数据,即如果集线器接收到来自A电脑的数据本来是想转发给B电脑,如果此时它还连接着另外两台电脑C、D,那么它会把这个数据给每个电脑都发送一份,因此会导致网络拥堵

3. 使用交换机组成一个网络

说明
  1. 克服了集线器以广播发送数据的缺点,当需要广播的时候发送广播,当需要单播的时候又能够以单播的方式进行发送
  2. 它已经替代了之前的集线器
  3. 企业中就是用交换机来完成多态电脑设备的链接成网络的

4. 使用路由器连接多个网络

5. 通信过程(复杂)

较为复杂的通信过程如:www.baidu.com

说明
  1. 在浏览器中输入一个网址时,需要将它先解析出ip地址来
  2. 当得到ip地址之后,浏览器以tcp的方式3次握手链接服务器
  3. 以tcp的方式发送http协议的请求数据 给 服务器
  4. 服务器tcp的方式回应http协议的应答数据 给浏览器

小结

  • MAC地址:在设备与设备之间数据通信时用来标记收发双方(网卡的序列号)
  • IP地址:在逻辑上标记一台电脑,用来指引数据包的收发方向(相当于电脑的序列号)
  • 网络掩码:用来区分ip地址的网络号和主机号
  • 默认网关:当需要发送的数据包的目的ip不在本网段内时,就会发送给默认的一台电脑,成为网关
  • 集线器:已过时,用来连接多态电脑,缺点:每次收发数据都进行广播,网络会变的拥堵
  • 交换机:集线器的升级版,有学习功能知道需要发送给哪台设备,根据需要进行单播、广播
  • 路由器:连接多个不同的网段,让他们之间可以进行收发数据,每次收到数据后,ip不变,但是MAC地址会变化
  • DNS:用来解析出IP(类似电话簿)
  • http服务器:提供浏览器能够访问到的数据

开放式系统互联参考模型

(Open System Interconnect) -->简称<front

这个标准定义了网络的七层框架,试图使得计算机在整个世界范围内实现互联。
在OSI中,网络体系结构被分成下面的七层。

  • 物理层
    • 定义了通信设备的传输规范,规定了激活、维持和关闭通信节点之间的机械特性、电气特性和功能特性等。此层为上层协议提供了一个传输数据的物理媒介。
  • 数据链路层
    • 定义了数据封装以及传送的方式。这个层次的数据单位称为“帧”。数据链路层包括两个重要的子层:逻辑链路控制层(Logic Link Control,LLC)和介质访问控制层(Media Access Control,MAC)。LLC用来对节点间的通信链路进行初始化,并防止链路中断,确保系统的可靠通信。而MAC则用来检测包含在数据帧中的地址信息。这里的地址是链路地址或物理地址,是在设备制造的时候设置的。网络上的两种设备不能有相同的物理地址,否则会造成网络信息传送失败。
  • 网络层
    • 定义了数据的寻址和路由方式。这一层负责对子网间的数据选择路由,并实现网络互连等功能。
  • 传输层
    • 为数据提供端到端传输。这是比网络层更高的层次,是主机到主机的层次。传输层将对上层的数据进行分段并进行端到端传输。另外,还提供差错控制和流量控制机制。
  • 会话层
    • 用来为通信的双方制定通信方式,包括建立和拆除会话。另外,此层将会在数据中插入校验点来实现数据同步。
  • 表示层
    • 为不同的用户提供数据和信息的转换。同时还提供解压缩和加解密服务。这一层保证了两个主机的信息可以互相理解。
  • 应用层
    • 控制着用户绝大多数对于网络应用程序的访问,提供了访问网络服务的接口。

[外链图片转存失败(img-7eDPqifK-1565512688964)(C:\Users\HP\Desktop\ddd.jpg)]

端口

整个网络通信通过IP

scoket简介

不同电脑上进程的标识与识别

用唯一标识标记一个进程。

在电脑上,可以通过进程号(PID)来唯一标识进程。但是在网络上,要TCP/IP协议。

用IP可以唯一标识网络中的主机,协议+端口号唯一标识主机中的应用进程。

进程指的是:运行的程序以及运行程序用到的资源的整体。

什么是scoket

进程间通信的一种方式。

与其他进程通信的一个主要不同是:

它能不同主机间的进程间的通信。

现行大多数服务器是基于Socket来完成通信的。

比如:浏览网页,QQ聊天,收发Email。

创建一个socket

import socket
socket.socket(AddressFamily, Type)

参数说明:

  • AddressFamily
    • AF_INET, internet间进程间通信,实际工作最常用。
    • AF_UNIX,同一台机器进程间通信
  • Type套接字类型
    • SOCK_DGRAM,数据报套接字,主要用于UDP协议
    • SOCK_STREAM,流式套接字,主要用于TCP协议

server.py

import socket

# 创建一个socket对象
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 主机名
host = socket.gethostname()
# 绑定端口
port = 9996
serversocket.bind((host,port))
# 设置最大连接数,超过后排队
serversocket.listen(5)

while True:
    # 简历客户端连接
    clientsocket,addr = serversocket.accept()
    print("连接地址:\t%s" %(str(addr)))
    msg = "网络编程测试"+"\r\n"
    clientsocket.send(msg.encode("utf-8"))
    clientsocket.close()

client.py

import socket

# 创建一个socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# print(dir(s))
# 获取本地主机名
host = socket.gethostname()
# 设置端口号
port = 9996
#连接服务,指定主机和端口
s.connect((host,port))
#接受小于1024字节的数据
msg = s.recv(1024)
s.close()
print(msg.decode('utf-8'))

Socket 对象(内建)方法

函数描述
服务器端套接字
s.bind()绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
s.listen()开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept()被动接受TCP客户端连接,(阻塞式)等待连接的到来
客户端套接字
s.connect()主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
s.connect_ex()connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv()接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
s.send()发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall()完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
s.recvform()接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
s.sendto()发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
s.close()关闭套接字
s.getpeername()返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname()返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value)设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen])返回套接字选项的值。
s.settimeout(timeout)设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout()返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.fileno()返回套接字的文件描述符。
s.setblocking(flag)如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.makefile()创建一个与该套接字相关连的文件

UDP网络程序

UDP --> User Datagram Protocol(用户数据报协议)是一个无连接的简单的面向数据报的运输层协议。

优缺点:

  • 优点:传递速度快(udp在传输时无需在客户端和服务器之间建立连接,也无超时重复发送机制)
  • 缺点:不能保证可靠性(udp是一种面向无连接的协议,每个数据都是一个独立的信息,包含完整的源地址或者目的地址,在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。)

特点:

UDP是面向无连接的通讯协议;

UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送

UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。

UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。

UDP是面向消息的协议,通信时不需要建立连接,数据的传输自然是不可靠的,UDP一般用于多点通信和实时的数据业务,比如

  • 语音广播
  • 视频
  • QQ
  • TFTP(简单文件传送)
  • SNMP(简单网络管理协议)
  • RIP(路由信息协议,如报告股票市场,航空信息)
  • DNS(域名解释)

发送数据

创建一个UDP客户端程序的流程:

1.创建一个客户端套接字

2.发送/接收数据

3.关闭套接字

sock和file的区别

  • file 针对指定模块进行“打开”,“读写”,“关闭”
  • sock针对服务端和客户端socket进行“打开”,“读写”,“关闭”

[外链图片转存失败(img-JmklU1qi-1565512688965)(C:\Users\HP\Desktop\11.png)]

[外链图片转存失败(img-X1K0B6qX-1565512688965)(C:\Users\HP\Desktop\22.png)]

from socket import *

#1. 创建套接字
udpSocket = socket(AF_INET, SOCK_DGRAM)
#2. 准备接收方的地址
Addr = ('192.168.1.124',8234)
#3. 从键盘获取数据
sendData = input("请输入要发送的数据:")
#4. 发送数据到指定的电脑上
udpSocket.sendto(sendData.encode('utf-8'), Addr)
#5. 等待接收对方发送的数据
recvData = udpSocket.recvfrom(1024) # 1024表示本次接收的最大字节数
#6. 显示对方发送的数据
print(recvData)
#7. 关闭套接字
udpSocket.close()

用网络调试助手时,端口号会一直变动。

UDP端口号绑定

一般情况下,在一台电脑上运行的网络程序有很多,为了不与其他的网络程序占用同一个端口号,往往在编程中,udp的端口号一般不绑定

但是如果需要做成一个服务器端的程序的话,是需要绑定的,想想看这又是为什么呢?

如果报警电话每天都在变,想必世界就会乱了,所以一般服务性的程序,往往需要一个固定的端口号,这就是所谓的端口绑定

import socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
local_addr = ("",7899)
udp_socket.bind(local_addr)

recv_data = udp_socket.recvfrom(1024)
print(recv_data[0].decode("gbk"))
udp_socket.close()
  • 一个udp网络程序,可以不绑定,此时操作系统会随机进行分配一个端口,如果重新运行次程序端口可能会发生变化

  • 一个udp网络程序,也可以绑定信息(ip地址,端口号),如果绑定成功,那么操作系统用这个端口号来进行区别收到的网络数据是否是此进程的

TCP简介

TCP协议,全称: 传输控制协议, 是一种面向连接的,可靠的、基于字节流的传输层通信协议。

TCP通信需要经过创建连接,传输数据,终止连接三个步骤。

TCP特点

面向连接

通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。

双方间的数据传输都可以通过这个连接进行。

完成数据交换后,双方断开此连接,以释放系统资源。

这种连接是一对一的

因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议

可靠传输

1)TCP采用发送应答机制

TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功

2)超时重传

发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。

TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。

3)错误校验

TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。

  1. 流量控制和阻塞管理

流量控制用来避免主机发送得过快而使接收方来不及完全收下。

TCP和UDP的不同点

  • 面向连接
  • 有序的数据传输
  • 无差错的数据传输(重发丢失的数据包,舍弃重复的数据包)
  • 阻塞/流量控制
  • TCP通信模型,类似"打电话",在通信开始前,一定要先建立相关连接,才能发送数据;而UDP通信模型,类似“写信,不需要建立相关连接,只需要发送数据即可”

在python中,用TCP进行socket编程也比较简单。

  • 客户端
    • 要主动连接服务器的IP和服务器的指定端口
  • 服务器
    • 监听指定端口
    • 对于每一个新的连接,创建一个线程或者进程

通常,服务器程序可以无限运行下去。要注意的是,一个端口不能同时被两个socket绑定。

TCP服务器和客户端各自socket的创建和交互。

[外链图片转存失败(img-I9N1w2nX-1565512688965)(C:\Users\HP\Desktop\852.png)]

tcp服务器

在python程序中,如果要完成一个tcp服务器的功能,需要的流程如下:

  • 1.socket创建一个套接字。(买手机)
  • 2.bind()绑定IP和Port(插卡)
  • 3.listen()使套接字由主动变为被动连接,即开启监听模式(设置一个响铃模式)
  • accept()等待客户端的连接。
  • recv/send 接受/发送数据
# tcp_server.py
import socket

# 创建套接字
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 绑定本地信息
tcp_socket.bind(("",8080))  #port num > 1024 的随意用,<1024 的一般指定了用途
# 设置监听
tcp_socket.listen(5)
# 创建新的套接字
new_socket,addr = tcp_socket.accept()
# 收/发数据
content = new_socket.recv(1024)
print("接收到的数据:",content.decode("gbk"))
data = input("服务器发送的数据:")
new_socket.send(data.encode("gbk"))
# 关闭通信的socket
new_socket.close() # 这个套接字关闭后,表示该套接字不再为当前客户端服务,需再创建新的套接字。
#关闭用于监听的套接字
tcp_socket.close()  # 该套接字关闭后不再接受新的客户端请求。

设置监听的目的:
socket默认为主动模式,listen()将其改为被动,被动才能接受信息
listen(5),5指最大可同时接受到客户端申请的最大数,超过则拒绝。

创建新套接字的原因:
服务器接收到请求后,将生成一个新的套接字,专门给新来的用户端提供一对一的服务,此时,服务器套接字的任务就是,等待新的客户端套接字连接它

# tcp_client.py

import socket

# 创建套接字
tcp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 目标服务器信息
server_ip =input("请输入服务器IP:")
server_port = int(input("请输入服务器port:"))
# 连接服务器
tcp_client_socket.connect((server_ip,server_port))
# 提示用户输入数据
send_data = input("请输入要发送的数据:")
tcp_client_socket.send(send_data.encode("utf-8"))
# 接受服务器端发来的信息
recv_data = tcp_client_socket.recv(1024)
print("接收到的数据为:",recv_data.decode("gbk"))

#关闭套接字
tcp_client_socket.close()

tcp注意点

  1. tcp服务端一般情况下都需要绑定,否则客户端找不到这个服务端
  2. tcp客户端一般不绑定,因为是主动连接服务端,所以只要确定好服务端的ip、port等信息就好,本地客户端可以随机
  3. tcp服务端中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务端时必须要做的
  4. 当客户端需要连接服务端时,就需要使用connect进行连接,udp是不需要连接的而是直接发送,但是tcp必须先连接,只有连接成功才能通信
  5. 当一个tcp客户端连接服务端时,服务端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务
  6. listen后的套接字是被动套接字,用来接收新的客户端的连接请求的,而accept返回的新套接字是标记这个新客户端的
  7. 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够连接服务端,但是之前已经连接成功的客户端正常通信。
  8. 关闭accept返回的套接字意味着这个客户端已经服务完毕
  9. 当客户端的套接字调用close后,服务端会recv解堵塞,并且返回的长度为0,因此服务端可以通过返回数据的长度来区别客户端是否已经下线

TCP协议

当应用程序希望通过TCP与另一个应用程序通信时,他会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方“握手”之后,TCP将在两个应用程序之间建立一个全双工(full-duplex)的通信。这个全双工的通信将占用两个计算机之间的通信线路,直到至少一方关闭为止。

标志位,TCP的6个标志位

URG 紧急指针,告诉接收TCP模块紧要指针域指着紧要数据。
ACK 置1时表示确认号(为合法,为0的时候表示数据段不包含确认信息,确认号被忽略。
PSH 置1时请求的数据段在接收方得到后就可直接送到应用程序,而不必等到缓冲区满时才传送。
RST 置1时重建连接。如果接收到RST位时候,通常发生了某些错误。
SYN 置1时用来发起一个连接。
FIN 置1时表示发端完成发送任务。用来释放连接,表明发送方已经没有数据发送了。

其中URG不能和PSH标志位同时使用。
URG为紧急数据标志,如果URG为1,表示本数据包中包含紧急数据。此时紧急数据指针表示的值有效,它表示在紧急数据之后的第一个字节的偏移值(即紧急数据的总长度)。
ACK为确认标志位。如果ACK为1,表示数据包中的确认号有效。
PSH位,表示强迫数据传输。
RST标志位用来复位一条连接。当RST=1时,表示出现严重错误,必须释放连接,然后再重新建立。
SYN标志位用来建立连接,如果SYN=1而ACK=0,表明它是一个连接请求;如果SYN=1且ACK=1,则表示同意建立一个连接。
FIN为1时,表示数据已经发送完毕,希望释放连接。

所谓标志位,是一种二进制的指令

  • SYN:发起一个新连接
  • FIN:释放一个连接
  • ACK:确认

TCP三次握手

所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:

img

(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

简单来说,就是
1、建立连接时,客户端发送SYN包(SYN=i)到服务器,并进入到SYN-SEND状态,等待服务器确认
2、服务器收到SYN包,必须确认客户的SYN(ack=i+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN-RECV状态
3、客户端收到服务器的SYN+ACK包,向服务器发送确认报ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手,客户端与服务器开始传送数据。

TCP四次挥手

所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:

img

由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。

(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。

(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

[外链图片转存失败(img-YvicKB3A-1565512688971)(C:\Users\HP\Desktop\woshou.jpg)]

  • 广播
  • Mac地址
  • arp协议
  • 路由器
  • 局域网
  • 子网掩码

https://www.cnblogs.com/dalaoban/p/9330620.html

多任务介绍

1.现实中的多任务

2.计算机中的多任务

操作系统同时完成多项任务的处理(同时 指 同一个时间段)

​ 多任务处理是指,用户在同一时间段内运行多个应用程序,每个应用程序就可以称之为一个任务。

现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?

答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。

真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

并发与并行
  • 并发处理(concurrency Processing):指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机(CPU)上运行,但任一个时刻点上只有一个程序在处理机(CPU)上运行
  • 并行处理(Parallel Processing):是计算机系统中能同时执行两个或更多个处理的一种计算方法。并行处理可同时工作于同一程序的不同方面。并行处理的主要目的是节省大型和复杂问题的解决时间。

并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以说,并行是并发的子集

[外链图片转存失败(img-PYylkEjD-1565512688971)(C:\Users\HP\Desktop\1234.png)]

进程

一个程序的执行实例。每个进程提供执行程序所需的所有资源。
进程本质上是资源的集合。

一个进程有虚拟的地址空间、可执行的代码、操作系统的接口、安全的上下文(记录启动该进程的用户和权限等)、唯一的进程ID、环境变量、优先级类、最小和最大的工作空间(内存空间),还要至少

在unix/linux中,提供了fork()系统函数。

  • 普通函数调用,调用一次,返回一次

  • fork()调用一次,返回两次。

    • 因为操作系统自动把当前的进程(父进程)复制一份(子进程),进程内返回。

    fork()子进程永远返回0,而父进程返回子进程的id

    一个父进程可以fork出很多子进程,并记下id,子进程也可以拿到父进程的id

  • getpid() 返回当前进程标识

  • getppid() 返回父进程标识

import os

pid = os.fork()
if pid < 0:
    print("fork 调用失败.")
elif pid == 0:
    print("我是子进程:\t%s, 我的父进程是:\t%s"%(os.getpid(),os.getppid()))
else:
    print("我是父进程:\t%s, 我的子进程是:\t%s" % (os.getpid(), os.getppid()))
print("父子进程都可以执行这里")

多进程修改全局变量

线程

线程是操作系统能够运算调度的最小单位。线程被包含在进程之中,是进程中的实际运作单位。一条线程是进程中的一个单一顺序的控制流,一个进程可以并发多个线程,每条线程并行执行不同的任务。

一条线程是一个execution context(执行上下文),即一个CPU执行时所需要的一串指令。

线程的工作方式

假设你正在读一本书,没有读完,你想休息一下,但是你想在回来时恢复到当时读的具体进度。有一个方法就是记下页数、行数与字数这三个数值,这些数值就是execution context。如果你的室友在你休息的时候,使用相同的方法读这本书。你和她只需要这三个数字记下来就可以在交替的时间共同阅读这本书了。

线程的工作方式与此类似。CPU会给你一个在同一时间能够做多个运算的幻觉,实际上它在每个运算上只花了极少的时间,本质上CPU同一时刻只干了一件事。它能这样做就是因为它有每个运算的execution context。就像你能够和你朋友共享同一本书一样,多任务也能共享同一块CPU。

进程与线程区别

1.同一个进程中的线程共享同一内存空间,但是进程之间是独立的。
2.同一个进程中的所有线程的数据是共享的(进程通讯),进程之间的数据是独立的。
3.对主线程的修改可能会影响其他线程的行为,但是父进程的修改(除了删除以外)不会影响其他子进程。
4.线程是一个上下文的执行指令,而进程则是与运算相关的一簇资源。
5.同一个进程的线程之间可以直接通信,但是进程之间的交流需要借助中间代理来实现。
6.创建新的线程很容易,但是创建新的进程需要对父进程做一次复制。
7.一个线程可以操作同一进程的其他线程,但是进程只能操作其子进程。
8.线程启动速度快,进程启动速度慢(但是两者运行速度没有可比性)。


开启多线程

python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用,通过threading模块可以创建线程

继承Thread类,创建一个新的Class,将要执行的代码写到run函数里

demo:

import threading
import time

#自定义类threading.Thread
class MyThread(threading.Thread):
    def run(self):
        for i in range(5):
            time.sleep(1)
            # name 保存的是当前线程的名字
            msg = "I am "+self.name +' @ '+str(i)
            print(msg)

if __name__ == '__main__':
    t1 = MyThread()
    t2 = MyThread()

    t1.start()
    t2.start()

python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread的Start()方法,可以启动该线程。当该线程获得执行机会时,会调用run方法执行线程。

threading.currentThread(): 返回当前的线程变量。 
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

多线程的注意点:

子线程何时开启,何时运行

当调用thread.start()时 开启线程,再运行线程的代码

子线程何时结束

子线程把target指向的函数中的语句执行完毕后,或者线程中的run函数代码执行完毕后,立即结束当前子线程

查看当前线程数量

通过threading.enumerate()可枚举当前运行的所有线程

主线程何时结束

所有子线程执行完毕后,主线程才结束

线程的执行顺序
多线程的创建与执行都是无序的。

总结

    每个线程默认有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。

    当线程的run()方法结束时该线程完成。

    无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。




t2 = MyThread()

t1.start()
t2.start()

python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread的Start()方法,可以启动该线程。当该线程获得执行机会时,会调用run方法执行线程。

```python
threading.currentThread(): 返回当前的线程变量。 
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

多线程的注意点:

子线程何时开启,何时运行

当调用thread.start()时 开启线程,再运行线程的代码

子线程何时结束

子线程把target指向的函数中的语句执行完毕后,或者线程中的run函数代码执行完毕后,立即结束当前子线程

查看当前线程数量

通过threading.enumerate()可枚举当前运行的所有线程

主线程何时结束

所有子线程执行完毕后,主线程才结束

线程的执行顺序
多线程的创建与执行都是无序的。

总结

    每个线程默认有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。

    当线程的run()方法结束时该线程完成。

    无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值