Python 学习笔记-第16讲:Web 编程

一、网络基础
1. 基础知识
网络根据地域可分为三类:局域网()、城域网(几十公里)、广域网(www)
IP地址是由4个8位组成,每一个数字不能大于255,
00000000.00000000.00000000.00000000
IP地址由网络IP+本机IP组成
分类五类:
A:1.0.0.0~126.255.255.255 (127.0.0.1,localhost是本地回环地址)例:111.2.1.123 网络IP:111.0.0.0
B:128.0.0.0~191.255.255.255
例:129.23.23.21 网络IP:129.23.0.0
C:192.0.0.0~223.255.255.255
例:192.123.12.9 网络IP:192.123.12.0
D:用于组播
E:
2. OSI 七层模型
 OSI(Open System Interconnect),即开放式系统互联。 一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互连模型。
OSI定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),每一层实现各自的功能和协议,并完成与相邻层的接口通信。
Python 学习笔记-第16讲:Web 编程

TCP/IP五层模型
    TCP/IP五层协议和OSI的七层协议对应关系如下。
Python 学习笔记-第16讲:Web 编程

应用层(表示层、会话层)
传输层:tcp udp
网络层:逻辑寻址,寻路径
数据链路层:硬件寻址
物理层:
查看本机IP:cmd--->ipconfig
电脑是否可以上网:ping ip地址
网络编程:实现计算机与计算机间的通信

3. 通信协议
tcp:可靠,有状态的,长连接的协议,像打电话一样
udp:不可靠,无连接,像发短信一样。发送的包的顺序要有编号
http:基于tcp的协议,无状态的协议
ftp:文件传输协议
pop3:邮局协议版本3”。是TCP/IP协议族中的一员
smtp:简单邮件传输协议
QQ视频,发消息

TCP与UDP基本区别
1.基于连接与无连接
2.TCP要求系统资源较多,UDP较少;
3.UDP程序结构较简单
4.流模式(TCP)与数据报模式(UDP);
5.TCP保证数据正确性,UDP可能丢包
6.TCP保证数据顺序,UDP不保证

收藏博客:TCP 和 UDP 的区别

4. Http 通信流程
一次HTTP操作称为一个事务,其工作过程可分为四步:

1)首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP的工作开始。

2)建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。

3)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。

4)客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。

如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。
IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层,
TPC/IP协议是传输层协议,主要解决数据如何在网络中传输
HTTP是应用层协议,主要解决如何包装数据。


二、Socket 网络通信接口
1. 概念
Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX  BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰)来实现网络进程之间的通信。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
知乎上的一段通俗讲解:
socket就是网络连接的端点。就像一根网线,一头连到路由器,一头连到电脑。这两端就是socket。
socket编程就是利用一些函数开发网络应用。这些函数都与socket接口有关。socket接口可以在各种系统上使用,比如linux,windows,mac。
从开发的角度看,socket类似文件。这与开发文件读写程序时你打开的文件类似,只不过这是一个socket文件。
socket的结构
socket的结构很简单,只有三个元素,协议,端口号,IP地址。
socket程序的样子?
在C语言中,socket函数能够返回一个socket描述符,把它想象成文件描述符。
接下来如果是客户端,使用connect连接socket地址,连接成功,socket描述符就可以读写了。
服务器端
bind函数将socket地址和socket描述符绑定。listen函数讲socket描述符转化成“监听描述符”,供服务器监听客户端的请求用。
accept函数等待客户端的请求,返回“已连接描述符”,后续可以用来与客户端通信(使用底层io函数)。

2. socket()函数
Python 中,我们用 socket()函数来创建套接字,语法格式如下:

socket.socket([family[, type[, proto]]])
参数
family: 套接字家族可以使AF_UNIX或者AF_INET
type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM
protocol: 一般不填默认为0.

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.recvfrom() 接收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() 创建一个与该套接字相关连的文件

有关 socket 基本过程的博文:

主要方法:
socket():用于创建一个socket描述符
bind():为socket对象绑定地址
listen()、connect():如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求
accept():服务器接收客户端的连接请求。
recv()、send():接收/发送TCP数据
close():关闭socket

使用 socket 模块的 socket 函数来创建一个 socket 对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。
服务器端:
通过调用 bind(hostname, port) 函数来指定服务的 port(端口)。
接着,调用 socket 对象的 accept 方法。该方法等待客户端的连接,并返回 connection 对象,表示已连接到客户端。
客户端:
socket.connect(hosname, port ) 方法打开一个 TCP 连接到主机为 hostname 端口为 port 的服务商。连接后我们就可以从服务端获取数据





3. TCP连接的三次握手
  第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
  第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
  握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。
  理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
  断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)
4. TCP 和 UDP 编程的一般步骤
TCP:
TCP编程的服务器端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt(); * 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();
  4、开启监听,用函数listen();
  5、接收客户端上来的连接,用函数accept();
  6、收发数据,用函数send()和recv(),或者read()和write();
  7、关闭网络连接;
  8、关闭监听;

TCP编程的客户端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
  4、设置要连接的对方的IP地址和端口等属性;
  5、连接服务器,用函数connect();
  6、收发数据,用函数send()和recv(),或者read()和write();
  7、关闭网络连接;

UDP:
与之对应的UDP编程步骤要简单许多,分别如下:
  UDP编程的服务器端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();
  4、循环接收数据,用函数recvfrom();
  5、关闭网络连接;

UDP编程的客户端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
  4、设置对方的IP地址和端口等属性;
  5、发送数据,用函数sendto();
  6、关闭网络连接;
三、线程
基本概念:
    线程:进程中的每个子任务,不能独立存在
    进程:独立的所有子任务的集合
    线程,进程:目的都是想同时完成任务
特点:
    进程的特点:独立(内存独立,cpu使用独立)启动进程开销大(速率低),进程之间很难共享数据,和数据通信,数据安全高。
def a():
加锁
b()
释放锁
def b():
加锁
a()
释放锁
线程的特点:依赖进程(内存共享,CPU使用独立)启动开销小,线程之间共享数据容易,方便通信,线程不安全。
python:
两种方式:函数和类
Python2:thread
Python3:_thread
    threading(功能相比_thread更强大,推荐使用)

调用 _thread 模块中的start_new_thread()函数来产生新线程。语法如下:

_thread.start_new_thread ( function, args[, kwargs] )
参数说明:

function - 线程函数。
args - 传递给线程函数的参数,他必须是个tuple类型。
kwargs - 可选参数。
或者重写父类的start()方法
threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
run(): 用以表示线程活动的方法。
start():启动线程活动。
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。